Elasticsearch
基本概念
定義
- 一個分布式的實時文檔存儲矾策,每個字段 可以被索引與搜索
- 一個分布式實時分析搜索引擎
- 能勝任上百個服務節(jié)點的擴展,并支持 PB 級別的結(jié)構(gòu)化或者非結(jié)構(gòu)化數(shù)據(jù)
用途
- 全文檢索
- 結(jié)構(gòu)化搜索
- 分析
VS傳統(tǒng)數(shù)據(jù)庫
- 傳統(tǒng)數(shù)據(jù)庫
- 提供精確匹配
- ES
- 提供精確匹配
- 全文檢索
- 處理同義詞
- 給文檔相關(guān)性評分
- 生成分析與聚合數(shù)據(jù)
- 實時
專有名詞
-
索引(名詞)
類似于數(shù)據(jù)庫
-
索引(動詞)
類似于insert震庭。例如索引一個文檔到一個索引
-
倒排索引
默認每個屬性都會有一個倒排索引鼻由,可以設(shè)置屬性不被索引担钮,它只能被覆蓋橱赠,不能被修改
-
類型
類似表,同一索引的不同類型箫津,可以擁有不同的字段狭姨,但應該擁有大部分相似的字段。它可以包含大小寫苏遥,不能包含句號饼拍,不能以下劃線開頭,長度限制為256.
-
Id
文檔的id田炭,可以在生成文檔時指定或自動生成师抄,自動生成的ID,在大部分情況下多個節(jié)點的時候唯一教硫。如果在創(chuàng)建文檔時叨吮,ID沖突辆布,服務器會返回409
-
文檔
類似于記錄,文檔只能被替換茶鉴,而不能被修改锋玲,文檔的字段類型需要一致,否則無法進行精確匹配
-
精確值字段
對于數(shù)字涵叮,日期惭蹂,布爾和一個not_analyzed字段,進行查詢围肥,會適用精確匹配
-
全文搜索字段
否則剿干,進行相關(guān)性搜索
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
例如
- megacorp是索引
- employee是類型
- 1是文檔id
- json內(nèi)容是文檔
交互
RESTful API
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
VERB |
適當?shù)?HTTP 方法 或 謂詞 : GET 、 POST 穆刻、 PUT 、 HEAD 或者 DELETE 杠步。 |
---|---|
PROTOCOL |
http 或者 https (如果你在 Elasticsearch 前面有一個 https 代理) |
HOST |
Elasticsearch 集群中任意節(jié)點的主機名氢伟,或者用 localhost 代表本地機器上的節(jié)點。 |
PORT |
運行 Elasticsearch HTTP 服務的端口號幽歼,默認是 9200 朵锣。 |
PATH |
API 的終端路徑(例如 _count 將返回集群中文檔數(shù)量)。Path 可能包含多個組件甸私,例如:_cluster/stats 和 _nodes/stats/jvm 诚些。 |
QUERY_STRING |
任意可選的查詢字符串參數(shù) (例如 ?pretty 將格式化地輸出 JSON 返回值,使其更容易閱讀) |
BODY |
一個 JSON 格式的請求體 (如果請求需要的話) |
例子
request:
curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
"query": {
"match_all": {}
}
}
'
response:
{
"count" : 0,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
}
}
檢索文檔功能
-
獲取一個文檔
GET /megacorp/employee/1
-
簡單查詢
GET /megacorp/employee/_search #查詢前十條記錄
GET /megacorp/employee/_search?q=last_name:Smith #查詢smith的前十條記錄
-
表達式查詢
GET /megacorp/employee/_search { "query" : { "match" : { "last_name" : "Smith" } } }
GET /megacorp/employee/_search { "query" : { "bool": { "must": { "match" : { "last_name" : "smith" } }, "filter": { "range" : { "age" : { "gt" : 30 } } } } } }
-
全文檢索
GET /megacorp/employee/_search { "query" : { "match" : { "about" : "rock climbing" } } }
-
短語查詢
GET /megacorp/employee/_search { "query" : { "match_phrase" : { "about" : "rock climbing" } } }
-
分析(類似于聚合group by)
request:
GET /megacorp/employee/_search { "aggs" : { "all_interests" : { "terms" : { "field" : "interests" }, "aggs" : { "avg_age" : { "avg" : { "field" : "age" } } } } } }
response:
... "all_interests": { "buckets": [ { "key": "music", "doc_count": 2, "avg_age": { "value": 28.5 } }, { "key": "forestry", "doc_count": 1, "avg_age": { "value": 35 } }, { "key": "sports", "doc_count": 1, "avg_age": { "value": 25 } } ] }
-
修改文檔
PUT /website/blog/123 { "title": "My first blog entry", "text": "I am starting to get the hang of this...", "date": "2014/01/02" }
舊文檔不會馬上刪掉
新文檔會被索引
文檔的version會加一
-
刪除文檔
DELETE /megacorp/employee/123
文檔的version依然會加一
分布式特性
ES自動執(zhí)行的分布式動作
- 分配文檔到不同的容器 或 分片 中皇型,文檔可以儲存在一個或多個節(jié)點中
- 按集群節(jié)點來均衡分配這些分片诬烹,從而對索引和搜索過程進行負載均衡
- 復制每個分片以支持數(shù)據(jù)冗余,從而防止硬件故障導致的數(shù)據(jù)丟失
- 將集群中任一節(jié)點的請求路由到存有相關(guān)數(shù)據(jù)的節(jié)點
- 集群擴容時無縫整合新節(jié)點弃鸦,重新分配分片以便從離群節(jié)點恢復
水平擴展VS垂直擴展
ES對水平擴展是友好的绞吁,通過購置更多的機器,可以更好的使用ES的分布式功能
集群
集群擁有一個或多個節(jié)點唬格,當有節(jié)點加入或者退出集群時家破,集群會重新平均分配所有數(shù)據(jù)的分布
主節(jié)點功能
增加/刪除索引
-
增加/刪除節(jié)點
不涉及文檔的變更和搜索,因此單一的主節(jié)點不會成為集群的性能瓶頸
索引分片的元數(shù)據(jù)在每個ES節(jié)點都有存儲购岗,每個節(jié)點在接到請求后汰聋,都知道到哪臺ES node找到數(shù)據(jù),通過轉(zhuǎn)發(fā)請求到ES node所在的機器
一個分片的最大文檔數(shù):(2^31-128)
一個索引的主分片數(shù)在建立時被確定喊积,且無法修改:因為文檔的存儲是用shard = hash(routing) % number_of_primary_shards來確定文檔的位置的烹困。routing默認是id,也可以自定義
分片的副本數(shù)可以隨時修改
建立索引
PUT /blogs
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
故障轉(zhuǎn)移
增加一臺機器到集群
刪除節(jié)點
分布式寫入沖突
對于多個client的寫入ES注服,有可能造成寫入沖突韭邓,導致數(shù)據(jù)的丟失
在一些場景下措近,數(shù)據(jù)丟失是可以接受的
但是在某些場景下,是不允許的女淑。
悲觀控制并發(fā)
傳統(tǒng)數(shù)據(jù)庫的控制方式瞭郑。通過對記錄加鎖,來實現(xiàn)并發(fā)的串行執(zhí)行
樂觀并發(fā)控制
ES采用樂觀控制
所謂樂觀控制鸭你,就是服務器假設(shè)大部分情況下屈张,是不會發(fā)生沖突的,如果發(fā)生沖突袱巨,則拒絕修改阁谆,客戶端可以需要通過重新獲取并重試進行處理。
過程如下圖
分布式文檔存儲
確定文檔位于那個shard
shard = hash(routing) % number_of_primary_shards
API支持帶routing參數(shù)愉老,來自定義路由场绿,來確保相關(guān)文檔路由到同一個分片
以ID新建,寫入和刪除文檔
一致性保證
- none: 主分片活躍嫉入,允許寫入
- all: 在所有分片活躍焰盗,允許寫入
- quorum: 半數(shù)以上節(jié)點活躍,允許寫入
如果暫時沒有足夠的分片活躍咒林,ES會等待熬拒,默認等待1分鐘,可以通過參數(shù)timeout改變這個值垫竞,如果超時澎粟,則失敗返回
新索引默認有
1
個副本分片,這意味著為滿足規(guī)定數(shù)量
應該 需要兩個活動的分片副本欢瞪。 但是活烙,這些默認的設(shè)置會阻止我們在單一節(jié)點上做任何事情。為了避免這個問題引有,要求只有當number_of_replicas
大于1的時候瓣颅,規(guī)定數(shù)量才會執(zhí)行。
以ID檢索文檔
與上圖類似
以ID更新文檔
與上圖類似譬正,但在更新完文檔后宫补,會重建索引
在局部更新文檔的時候,主分片會以整份文檔來同步給副本曾我,來保證數(shù)據(jù)的完整性
通過條件獲取多個文檔
搜索
返回特殊字段
GET /_search
{
"hits" : {
"total" : 14,
"hits" : [
{
"_index": "us",
"_type": "tweet",
"_id": "7",
"_score": 1,
"_source": {
"date": "2014-09-17",
"name": "John Smith",
"tweet": "The Query DSL is really powerful and flexible",
"user_id": 2
}
},
... 9 RESULTS REMOVED ...
],
"max_score" : 1
},
"took" : 4,
"_shards" : {
"failed" : 0,
"successful" : 10,
"total" : 10
},
"timed_out" : false
}
-
took
執(zhí)行的毫秒數(shù)
-
_shards
查詢分片的狀態(tài)粉怕,例如有幾個分片是失敗的,幾個是成功的
-
timeout
可以通過在查詢設(shè)定超時抒巢,如果查詢超過時間贫贝,則只返回已經(jīng)成功獲得的數(shù)據(jù),剩余的數(shù)據(jù)將丟棄
-
_index
數(shù)據(jù)來源的Lucene索引,文檔的每一個字段稚晚,都擁有一個不同的Lucene索引
在查詢中可以指定Lucene的索引崇堵,默認是不指定的,所以會查詢該文檔的所有索引客燕,并匯總結(jié)果鸳劳。如果指定,則會限定僅在指定的Lucene索引中查詢數(shù)據(jù)
分頁
GET /_search?size=5&from=5
此方式只適用于淺分頁也搓,如果查詢過深赏廓,會導致嚴重的性能問題。
因為例如查詢size為5傍妒,from=10000幔摸。那么ES會從各分片中都查詢10005條記錄,如果有100個shard颤练,那么就會有100*10005條記錄既忆,ES再對這100*10005排序,并僅返回5條記錄
深分頁
使用游標scroll
它在ES中建立了一個有有效期的快照嗦玖,提供給scroll進行數(shù)據(jù)的深度查詢
倒排索引
對一下文檔進行倒排:
- The quick brown fox jumped over the lazy dog
- Quick brown foxes leap over lazy dogs in summer
得到:
Term Doc_1 Doc_2
-------------------------
Quick | | X
The | X |
brown | X | X
dog | X |
dogs | | X
fox | X |
foxes | | X
in | | X
jumped | X |
lazy | X | X
leap | | X
over | X | X
quick | X |
summer | | X
the | X |
------------------------
倒排面臨的挑戰(zhàn)
- Quick跟quick尿贫,用戶有可能認為它們是相同的,也有可能認為是不同的
- dog和dogs非常接近踏揣,在相關(guān)性搜索時,它們應該都被搜索到
- jump和leap是同義詞匾乓,在相關(guān)性搜索時捞稿,它們應該都被搜索到
理解相關(guān)性
相關(guān)性的分數(shù)是一個模糊的概念。沒有精確值拼缝,沒有唯一正確的答案娱局。是一種根據(jù)各種規(guī)則對文檔進行的一種量化的估計。
它評分的準則如下:
-
檢索詞頻率
檢索詞在該字段出現(xiàn)的頻率越高咧七,分數(shù)越高
-
反向文檔頻率
檢索詞在索引出現(xiàn)的頻率越高衰齐,分數(shù)越低
-
字段長度準則
字段的長度越長,分數(shù)越低
相關(guān)性破壞
在使用全文檢索某個關(guān)鍵字的時候继阻,會出現(xiàn)耻涛,相關(guān)度低的文檔的得分高于相關(guān)度高的文檔的得分。
例如檢索詞milk瘟檩。索引內(nèi)有兩個主分片抹缕,milk在P1出現(xiàn)了5次,在P2出現(xiàn)了6次墨辛。由于P1和P2的詞分布不一樣卓研。
P1的詞量比P2的詞量高,那么milk算在P1出現(xiàn)占比小,導致在P1得相關(guān)性得分高奏赘,而在P2寥闪,占比da,導致在P2的相關(guān)性得分低磨淌。
原因
是因為局部數(shù)據(jù)分布不均勻?qū)е碌?/p>
解決方法
- 插入更多的文檔
- 使用?search_type=dfs_query_then_fetch進行全局評分疲憋。但會有嚴重的性能問題。不推薦使用伦糯。
查詢過濾bitset
每次使用檢索詞查詢柜某,都會為檢索詞建立一個bitset,bitset包含了匹配的文檔的序號敛纲。在熱搜索的檢索詞喂击,ES會對這些bitset有針對的進行緩存,而不用在再次查詢的時候淤翔,重新查找倒排索引翰绊。
對于多個查詢可以有下圖
當?shù)古潘饕亟ǖ臅r候,bitset在緩存會自動失效
緩存的策略
- 最近256次被使用的bitset旁壮,會被緩存
- 段內(nèi)記錄小于1w的监嗜,不會被緩存
索引管理
創(chuàng)建索引
可以顯式創(chuàng)建,也可以隱式創(chuàng)建抡谐。
在大集群下裁奇,索引的創(chuàng)建,涉及元數(shù)據(jù)的同步麦撵,有可能導致集群負載的大量增加刽肠。此時需要禁用索引的隱式創(chuàng)建
action.auto_create_index: false
刪除索引
刪除索引,會涉及大量數(shù)據(jù)的刪除免胃,如果用戶意外地試圖通過一條命令音五,把所有索引刪掉,這可能導致可怕的后果
通過禁用此操作羔沙,可以設(shè)置如下
action.destructive_requires_name: true
分析器
每個索引都可以設(shè)置自己的分析器躺涝,分析器的用途主要是在全文索引上面,通過對不同的語言扼雏,使用不同的分詞坚嗜,不同的詞轉(zhuǎn)換來構(gòu)造倒排索引和計算相關(guān)性。
分片
倒排索引的不變性
好處
- 一旦被讀入系統(tǒng)緩存呢蛤,就會一直留在那里惶傻,直到LRU算法把不常用的倒排索引剔除。這對ES的讀取性能提供了非常大的提升
不好
- 新的文檔加入其障,不能增量更新银室,只能重建索引并替換
如何保證新數(shù)據(jù)能實時能查詢到
用更多的索引。
對于新的文檔,不馬上重建索引蜈敢,而是通過新增額外的索引辜荠。在查詢數(shù)據(jù)時,通過輪詢所有的索引抓狭,并合并結(jié)果返回伯病。
ES并不是嚴格意義上的實時,準確來說是準實時否过,由于data從插入到建立倒排索引這段時間午笛,新數(shù)據(jù)是不能訪問的
聚合
像數(shù)據(jù)庫的group by。只是語法不一樣苗桂。功能相通
應用層性能調(diào)優(yōu)
調(diào)大 refresh interval
默認刷新時間是1s药磺,每次刷新都會有一次磁盤寫入,并創(chuàng)建一個新的段煤伟。通過設(shè)置更大的刷新時間癌佩,可以讓磁盤寫入的次數(shù)更低,寫入的段更大便锨。減少段合并的次數(shù)围辙。
禁止OS把ES置換出去
OS的內(nèi)核會在內(nèi)存緊張的時候,把進程置換到外村放案。而對于性能跟內(nèi)存強相關(guān)的ES來說姚建,置換到外存是致命的。通過設(shè)置進程在內(nèi)核的參數(shù)吱殉,禁止置換桥胞,可以避免OS的這種動作
預留大量的文件系統(tǒng)緩存給ES
由于ES大部分數(shù)據(jù)的不變性,使得ES的大部分磁盤操作考婴,都可以通過文件系統(tǒng)的緩存來加快速度。一旦ES的倒排索引和數(shù)據(jù)緩存到系統(tǒng)催烘,如果沒有其他進程的干擾沥阱,而且是比較頻繁訪問的數(shù)據(jù),則會一直駐留在系統(tǒng)緩存伊群,使得ES的大部分操作都是走內(nèi)存的考杉。一般來說,分配一半的內(nèi)存給文件系統(tǒng)舰始,是合適的崇棠。
使用自動生成ID
如果指定ID,ES會在集群內(nèi)檢查是否ID已經(jīng)存在丸卷,這對大集群來說枕稀,是昂貴的。如果ID是自動生成的,ES會跳過檢查萎坷,直接插入文檔
更好的硬件
- 更大的內(nèi)存
- SSD
- 本地磁盤
不要使用join關(guān)聯(lián)查詢
ES不適合做關(guān)聯(lián)查詢凹联,會導致嚴重的性能問題。
如果業(yè)務一定要join哆档,可以把關(guān)聯(lián)的數(shù)據(jù)都寫到一個索引內(nèi)蔽挠,或者通過應用程序來做關(guān)聯(lián)的動作。
強制merge只讀索引
merge成一個單一的段瓜浸,會得到更好的性能
增加副本
有更多的機器澳淑,通過提高副本數(shù),可以提高讀效率
不要返回大數(shù)據(jù)
ES不適合這場景
避免稀疏
不要把不相關(guān)的信息存入同一個索引
數(shù)據(jù)預熱
對于熱點數(shù)據(jù)插佛,可以通過一個客戶端請求ES杠巡,讓數(shù)據(jù)先占據(jù)filesystem cache。
冷熱數(shù)據(jù)分離
冷熱數(shù)據(jù)部署在不同的機器朗涩,可以讓熱數(shù)據(jù)在緩存內(nèi)不會被冷數(shù)據(jù)沖走
內(nèi)核層性能調(diào)優(yōu)
限流
如果ES出現(xiàn)高負載的請求忽孽,ES的協(xié)調(diào)節(jié)點會累積大量的請求在內(nèi)存在等待處理,隨著請求數(shù)的增加谢床,協(xié)調(diào)節(jié)點的內(nèi)存占用會越來越大兄一,最后導致OOM。
通過限流识腿,可以有效緩解出革。
大查詢
如果客戶端發(fā)來了一個復雜的查詢,使得需要返回的數(shù)據(jù)異常的大渡讼,這也會導致OOM問題骂束。
通過修改內(nèi)核,讓如果請求的內(nèi)存占用超過系統(tǒng)可以承受的范圍成箫,則截斷來解決
FST過大引發(fā)OOM
FST是對倒排索引在內(nèi)存的索引展箱,它通過前綴狀態(tài)機的方法,快速的定位檢索詞在倒排索引的磁盤位置蹬昌,達到減少磁盤訪問次數(shù)而加快檢索速度的目的混驰。
但由于FST是常駐內(nèi)存的,如果倒排索引達到一定規(guī)模時皂贩,F(xiàn)ST必然會引起OOM問題栖榨。而且FST是存放在JVM堆內(nèi)內(nèi)存的。堆內(nèi)內(nèi)存的上限時32G明刷。
而10 TB的數(shù)據(jù)就需要10G到15G的內(nèi)存來存放FST婴栽。
- 通過把FST的存儲放到堆外內(nèi)存
- 通過LRU算法來管理FST,對不常用的FST置換出內(nèi)存
- 修改ES訪問FST的邏輯辈末,使得ES可以從堆內(nèi)直接訪問堆外的FST
- 在堆內(nèi)增加FST的cache愚争,加快命中速度
Ref: