前一篇文章描述了數(shù)據(jù)的獲?。ú杉┖皖A(yù)處理,本文介紹數(shù)據(jù)的存儲
存儲設(shè)計(jì)
為了實(shí)現(xiàn)更加快速便捷的數(shù)據(jù)訪問,需要對數(shù)據(jù)存儲方式進(jìn)行一定的設(shè)計(jì)。雖然目前只有251個景點(diǎn),但是系統(tǒng)應(yīng)該采用適用考慮更大規(guī)模數(shù)據(jù)的設(shè)計(jì)。
設(shè)計(jì)目標(biāo)
存儲的核心是索引,索引設(shè)計(jì)的關(guān)鍵就是選擇合適的索引技術(shù)。根據(jù)我們的目標(biāo):
- 景點(diǎn)查找,需求可以理解為根據(jù)名稱、地區(qū)、排名、是否可用等條件進(jìn)行檢索
- 附近景點(diǎn):即為需要根據(jù)經(jīng)緯度進(jìn)行查找
- 我是否來過?這個最簡單,需要一張記錄表。
總的來說,系統(tǒng)需要支持根據(jù)關(guān)鍵詞(名稱或地區(qū))、時間范圍、true/false、經(jīng)緯度等查詢。顯然,這需要用到全文索引、范圍索引、經(jīng)緯度索引,當(dāng)然還有哈希索引,這基本是所有系統(tǒng)必備的,它實(shí)現(xiàn)的就是根據(jù)ID進(jìn)行查找。
存儲選型
傳統(tǒng)的關(guān)系數(shù)據(jù)庫基本上支持范圍索引和哈希索引,盡管新版本也開始支持全文,但是畢竟這些數(shù)據(jù)庫大部分是國外的,對中文支持不好。
ElasticSearch(簡稱ES,別和EcmaScript搞混了)是一個很直接的選擇,能夠支持所有四種索引,并且建索引的過程是高度可控的。ES提供了RESTFul訪問接口,可以方便快速構(gòu)建Web應(yīng)用。
數(shù)據(jù)庫準(zhǔn)備
ES安裝部署
安裝非常簡單,到官網(wǎng)下載壓縮包,解壓后即可運(yùn)行。
啟動:
bin\elasticsearch.bat
通常需要改一下配置文件 config/elasticsearch.yml。主要配置項(xiàng)如下:
cluster.name: es7-2019
node.name: node-1
path.data: data
path.logs: logs
network.host: 127.0.0.1
http.port: 9200
xpack.ml.enabled: false
http.cors.enabled: true
http.cors.allow-origin: "*"
最底下的http.cors. 配置是為了后面安裝的head插件跨域訪問的,該插件相當(dāng)于是ES的管理界面。
head插件安裝
在2.x以前版本,head插件是放在ES的plugins目錄下,使用http://localhost:9200/_plugin/head訪問。版本5以后head插件需要單獨(dú)運(yùn)行。
參考這里 下載安裝運(yùn)行head插件。該項(xiàng)目基于npm+grunt,需要提前安裝nodejs。
由于前面已經(jīng)配置了ES支持跨域請求,所以可以看到看到界面:
這是我已經(jīng)寫入數(shù)據(jù)(place庫)后的樣子??梢钥吹较到y(tǒng)中有兩個索引(相當(dāng)于數(shù)據(jù)庫)place和bank。集群健康狀態(tài)是黃色,因?yàn)閎ank索引聲明了1一個副本,但是集群只有一個節(jié)點(diǎn)(node-1),只有一份數(shù)據(jù)。place索引是完全正常的,它分為5塊(12345),沒有副本。
圖中顯示了采用基本采用,loc字段匹配“延慶”的結(jié)果。這正式全文索引實(shí)現(xiàn)的效果,因?yàn)樗鼤Α把討c區(qū)”、“北京延慶”等進(jìn)行拆分,獲得“延慶”詞條。檢索時是基于該詞條進(jìn)行查找的。
分詞插件
ES 7版本自帶了一個中文分詞器 smartcn,效果還未檢驗(yàn)。之前一般用IK分詞器,這個插件需要單獨(dú)安裝。安裝比較簡單,參考https://github.com/medcl/elasticsearch-analysis-ik
數(shù)據(jù)處理入庫
基于存儲設(shè)計(jì),現(xiàn)在來實(shí)現(xiàn)數(shù)據(jù)入庫建索引。
文檔結(jié)構(gòu)
基于之前采集的信息和需要查詢的字段,設(shè)計(jì)如下文檔結(jié)構(gòu):
{ "loc": "北京.豐臺",
"name": "北戲書館",
"img": "http://zglynk.com/ITS/upload/areaIcon/3512bab5-3437-4821-bb48-edea8a185dfc.jpg",
"url": "http://zglynk.com/ITS/wechatPortalInfo/goAreaDetail.action?id=370",
"price": 20,
"stop": false,
"detail": "不限次 2018.11.1-2019.12.31(每周六接待)",
"rank": null,
"times": 0,
"phone": "010-67572221-2197、18612500357",
"time": "每周六19:00-21:00 2018.11.1-2019.12.31",
"geo": [116.377354, 39.85597 ]
}
ES中對于記錄唯一標(biāo)識_id是在整個文檔的外層。_id采用原網(wǎng)站的id,如果不存在,由ES生成一個UUID。
ES Mapping
ES默認(rèn)會自動識別文檔字段類型,建立相應(yīng)的索引,但是對于一些文本和經(jīng)緯度,由于檢索方式多樣,因此需要自己定制。索引設(shè)置index-setting.json如下:
{
"properties":{
"name": {"type": "text", "analyzer": "ik_smart"},
"detail": {"type": "text", "analyzer": "smartcn"},
"loc": {"type": "text", "analyzer": "ik_smart"},
"img": {"type": "text", "index": false},
"url": {"type": "text" , "index": false},
"rank": {"type": "text", "analyzer": "keyword"},
"geo": {"type": "geo_point"}
}
}
這里對name/detail/loc三個字段采用了全文檢索。不過為了試驗(yàn)分詞器效果,我分別采用了smartcn和IK。對于img和url字段,由于不需要檢索,因此不建索引。對于rank字段,它本身就是一個詞(2A 3A),因此類型是keyword。geo字段設(shè)置類型為geo_point,ES為該類型簡歷geohash索引,支持經(jīng)緯度范圍查詢。
注意,一般的數(shù)值、bool字段不需要特殊設(shè)置,ES會自動建立合適的索引。另外,ES還有很多參數(shù),包括_all _source、多字段、嵌套字段、數(shù)組等,要想用好,通讀一遍官方文檔很重要!
創(chuàng)建索引
索引基本設(shè)置:index-setting.json
{
"settings" : {
"index" : {
"number_of_shards" : 5,
"number_of_replicas" : 0
}
}
}
本自己本機(jī)做測試,沒有副本,分區(qū)也較少。在數(shù)據(jù)規(guī)模較大時,應(yīng)該設(shè)置較多的shard數(shù)。同時為了數(shù)據(jù)安全,應(yīng)該設(shè)置1-2個副本(前提是分布式集群部署)
通過curl命令創(chuàng)建:
host=localhost:9200
indexn=place
HEADER="Content-Type: application/json"
PREFIX="http://$host/$indexn"
curl -XPUT -H "$HEADER" $PREFIX -d "@index-setting.json"
curl -XPUT -H "$HEADER" ${PREFIX}/_mapping -d "@index-mapping.json"
小技巧:使用curl命令的 -d "@filename" 可以將復(fù)雜的body參數(shù)寫在文件里面。
另外,我用的ES 7版本提示必須要加Header,默認(rèn)的Content-Type: application/x-www-form-urlencoded 是不支持的。
數(shù)據(jù)處理與提交
將之前生成獲取的數(shù)據(jù),可以很容易地生成相應(yīng)的文檔格式。ES入庫可以一條一條執(zhí)行PUT請求,也可以使用它的bulk API。
為了使用bulk API,先將數(shù)據(jù)生成xjson格式:
{"index": {"_id": "370", "_index": "place"}}
{"loc": "北京.豐臺", "name": "北戲書館", "img": "http://zglynk.com/ITS/upload/areaIcon/3512bab5-3437-4821-bb48-edea8a185dfc.jpg", "url": "http://zglynk.com/ITS/wechatPortalInfo/goAreaDetail.action?id=370", "price": 20, "stop": false, "detail": "不限次 2018.11.1-2019.12.31(每周六接待)", "rank": null, "times": 0, "phone": "010-67572221-2197、18612500357", "time": "每周六19:00-21:00 2018.11.1-2019.12.31", "geo": [116.377354, 39.85597]}
{"index": {"_index": "place"}}
{"loc": "豐臺區(qū)", "name": "豐臺雪鄉(xiāng)冰雪嘉年華", "price": 120, "stop": true, "detail": "接待結(jié)束", "rank": null, "times": 0}
創(chuàng)建索引時,每條數(shù)據(jù)分為2行,第一行指明操作及索引名和_id,第二行是需要寫入的文檔。換行采用\n。
為了生成這個格式,python函數(shù):
def writeJsonArray(arr, filename):
with open(filename, 'w') as f:
for item in arr:
f.write(json.dumps(item, ensure_ascii = False)) #不要格式化
f.write('\n') #添加換行符
最后,提交整個文件給ES:
curl -s -H "Content-Type: application/x-ndjson" -XPOST localhost:9200/_bulk --data-binary "@list2.json"
小節(jié)
通過ES可以很方便地檢索查詢數(shù)據(jù)。不過ES本身是近實(shí)時的,采用最終一致性模型,因此不適合做一些事務(wù)類的業(yè)務(wù),因此主要用于數(shù)據(jù)查詢分析,尤其是全文檢索、日志分析等。
下一步
下一步將基于vue.js框架,構(gòu)建基本查詢檢索界面。通過openlayers或地圖平臺(如高德)API 進(jìn)行地圖展示。