為什么使用 ES?
在傳統的數據庫中,如果使用某列記錄某件商品的標題或簡介。在檢索時要想使用關鍵詞來查詢某個記錄,那么是很困難的,假設搜索關鍵詞 "小米",那么 sql 語句就是
select * from product where title like concat("%","小米","%")
這樣即使 title 列上包含索引,索引也會失效。而如果使用全文索引,因為 B+ 樹不支持全文索引,所以選擇了全文索引就失去了 B+ 遍歷高效的優點。所以 ES 就登場了,ES 之所以能高效檢索,主要原因就是其倒排索引的特點。常規的索引,也就是正向索引,查詢的過程是獲取整條數據,然后從整條數據中來匹配關鍵詞,如果包含就返回。而倒排索引是將數據拆分成多個關鍵詞,每個關鍵詞都作為一個倒排索引,然后查詢時直接判斷匹配,如果存在就返回該數據。這樣因為使用了索引效率就極大的提高了。
概念
索引:相當於 MySQL 的庫概念。
類型:相當於 MySQL 的表概念,在 ES7被移除。
文檔:相當於 MySQL 的行記錄概念。
字段:相當於 MySQL 的列概念。
分片:將某一類字段的文檔拆分出來作為一個分片,查詢時如果是這個字段的,直接去這個分片里查,可以提高系統整體的吞吐量。
副本:分片的復制,可以提高吞吐量(查詢請求可以直接走副本)和分區容錯性(分片所在的節點宕機后包含該分片副本的節點可以代替分片作用)
語法結構
query條件
Match:匹配查詢
Balance:用於匹配的字段(相當於列名),會對條件值進行分詞,再去 查詢,條件之間是 or 關系
字段后面加一個“.keyword”表示查詢完全匹配的字段。以address:”abc”為例,address必須為abc才算匹配。
Match_phrase:短語匹配
和 match 一樣也是先分詞再查詢,只不過條件是 and 關系
{
"query": {
"match_phrase": {
"content" : {
"query" : "我的寶馬多少馬力",
"slop" : 1 #允許少匹配的分詞個數
}
}
}
}
Match_all:所有字段匹配,其相反的是 match_none
Multi_match:多字段匹配
match 匹配的多字段匹配,多個字段只要有一個包含就滿足,返回,同時也支持分詞匹配
Address、city列只要有一個包含 mill和movico其中一個就滿足
Bool:復合查詢(多條件復雜查詢)
Must:必須滿足的
Match:匹配查詢,字符串模糊查詢,數字精確查詢
Must_not:必須不滿足
Should:可以滿足可以不滿足,滿足的得分更高,排在前面。
Filter:與must一樣,但是不會貢獻得分
Term:精確匹配
精確匹配,查詢完全匹配的字段值所對應的文檔
Terms:類似於 term,匹配多個值
如果字段是給定幾個值中的一個就返回
其他
分頁,指定返回的字段
結果分析
聚合
聚合就是在查詢結果的基礎上,進行分組統計,聚合可以迭代。
AVG:平均值聚合
Terms:類型聚合
...等等
Aggs:表示是聚合,與query一樣
ageAgg:聚合名,
Terms:類型聚合
Field:字段名
Size:取多少種
結果分析
Key:年齡
Doc_count:結果數
ageAvg:子聚合
Value:子聚合的值
Mapping 映射
映射主要指的是 ES 字段的單位。主要包括 Integer、long、keyword、text、nested(嵌入式,防止扁平化處理)。
查看某個索引下的映射
Get /bank/_mapping
添加索引並指定其字段映射
為某個索引添加新的字段並指定映射
修改字段映射
不支持對已存在的索引進行映射修改。可以使用數據遷移來完成。
1、創建臨時索引
2、將之前的索引數據遷移到創建的臨時索引中。
Source:原來的索引, index 來指定索引名也可以在index后面指定type,但是因為7開始移除了類型(相當於數據庫表),所以不需要指定了。
Dest:要轉移的索引名, index 來指定索引名。以當前例子來看就是 newbank
3、將原索引刪除,再創建新的索引,指定映射。
DELETE bank
4、最后將臨時索引數據遷移到新創建的索引中。
扁平化
由於扁平化的占用,在檢索 first 為 John,last 為 white 的文檔時,也會檢索到。所以對於子類中包含兩個或以上屬性的,應該將父類字段映射設為 nested 類型來防止 ES 的扁平化處理。(默認空間的不會進行扁平化,也就是properties下第一層的不會)
例子如下:
映射:
查詢:
聚合:
映射結果分析
分詞
分詞是 ES 的倒排索引,所以分詞器就決定了 ES 的倒排索引是什么,默認的分詞器是選擇空格隔開的英文作為一個分詞,中文的話每一個字都會是一個分詞。測試分詞效果:
如果想使用常用的中文分詞,可以使用 ik 分詞器,可以滿足絕大多數的中文分詞,而對於一些特殊的分詞,可以使用配置自定義的分詞,然后將保存自定義分詞的文件配置到 ik 分詞器中。
原理
ES架構
ES 是一個開源的高拓展的分布式全文搜索引擎,這句話表現出 ES 的兩個重要特點,全文搜索和高拓展分布式。其中全文搜索可以通過倒排索引體現出,而高拓展的分布式則可以通過其架構體現出。上面就是一個 ES 集群的架構圖,Node1、Node2、Node3 是三個節點服務器,其中 Node1 是主節點。在 ES 中配置了三個分片(P0、P1、P2,這三個分片保存着 ES 整個數據),同時,為了保證 ES 的分區容錯性以及查詢效率,每個分片還配置了一個副本(分別是 R0、R1、R2),原分片處理讀和寫操作,副本處理讀操作。在分布中將副本與原分片拆開放置,避免某個節點宕機該分片的數據無法使用。並且在增加節點后,集群會自動分配分片和副本,保證均勻分布在不同的節點上。比如:
單節點:
二節點:
三節點:
操作過程
存儲:根據存儲數據的 hash 取余計算分配的節點位置,選擇分片進行保存,隨后再將保存數據更新到所有副本中。默認情況下當大多數副本都同步完成時,就返回存儲完成的通知。這個可以通過 consistency 參數配置,all 表示必須所有的 副本都同步完成才返回存儲完成的通知,one 表示只要主分片同步完成就返回存儲完成的通知(檢索結果可能會被還未同步的副本處理,造成未檢索到),默認值是 quornum。
查詢:輪詢數據所在的分片和副本,發送請求。
更新:和存儲一樣,取余得到節點位置,找到分片后更新,再同步到其他副本,等到所有副本都同步完成后再返回更新成功的提示。
倒排索引的結構:倒排索引是無法修改的,好處是不用擔心讀寫不一致的問題,但是缺點也非常明顯,會大量的占用空間。為了減少空間占用,引入了段的概念,每個倒排索引都擁有一個段,在每次更新時都會將補充索引寫入段中,然后檢索時就會結合段中的數據和補充索引返回數據。
刪除:每個段中都有一個 .del 文件,當該倒排索引被下達刪除請求后,就會在 .del 文件進行標記,隨后檢索就會跳過當前段,也就是邏輯刪除。當倒排索引特別多時,會進行合並,此時會將那些邏輯刪除的段徹底刪除。
持久化:ES 數據的保存和檢索都在內存中,這也是它檢索速度快的原因之一,而其也會定期持久化到磁盤。
在分片執行更新、保存數據時,底層還伴隨着定期持久化,在寫入時,會先更新內存,隨后寫入內存中的 translog 里(避免斷電導致內存數據丟失,類似於 mysql 中的 redo log)。然后進行 refresh(默認1s執行一次)到文件系統緩存,更新到系統緩存后數據才能被檢索到。並且后台還會定期 flush(默認30min執行一次) ,將數據持久化到磁盤上。
本文主要參考某谷的ES視頻