1 基本概念
- cluster 類比成數據庫
- index 類比成表
- document 類比成表中一行數據
- field 類比成表中字段,字段是包含數據的鍵值對
2 ES 是如何實現分布式的?
- 一個索引其實會被分片成多個shard放在不同機器上,每個shard只有索引的部分數據。
- 每個shard可有多個replica shard 放在其他機器。primary shard負責讀寫,寫完后會同步到replica shard上。replica shard負責分擔讀請求,并防止其primary shard節點故障導致數據丟失。索引創建完成后,primary shard的數量就確定了不可更改,但replica shard的數量可以隨時調整。
- cluster中有多個node, 會自動選舉出一個node為master節點,負責維護索引元數據,負責集群中primary shard和replica shard的身份切換。
比如上圖,index分成p0 p1 兩個primary shard, 分別存儲在node3 和 node 1上,他們的replica shard各自都有兩份,比如p0的replica shard是r0, 在node1 和 node2 上。
cluster選舉了node1作為master節點。
primary shard的默認數量是5,replica默認是1,也就是說默認5個primary shard,5個replica shard
3 ES寫入數據的工作原理是什么?
插入、刪除和索引都是寫入操作。寫入操作的主要過程如下:
- 客戶端請求到任意節點,比如node1
- node1根據文檔_id參數,hash計算出分片位置在node3節點的p0上,于是轉發請求到p0
- node3的p0分片執行寫請求,完畢后轉發給自己的兩個分片r0。
- 等待兩個復制分片寫入成功,node3報告寫入成功給node1節點,node1節點再告訴客戶端寫入成功。
其中“執行寫請求”的底層原理如下。
3.1 refresh
在ES中,buffer每隔1秒(或者滿了),打開一個新segemnt并寫入的過程,叫做refresh。
默認情況下,每個分片每秒自動刷新一次。這就是為什么說Elasticsearch是近實時的搜索了:
文檔的改動在refresh之前,是搜索不出來的。
3.2 flush
在ES中,進行一次提交并刪除事務日志的操作叫做 flush 。分片每30分鐘,或事務日志過大,都會進行一次flush操作。
3.3 translog
為了數據安全es默認每隔5秒鐘會把translog刷新(fsync)到磁盤中,也就是說最多會丟失5秒鐘的數據,如果你對數據安全比較敏感,可以把這個間隔減小,但是會占用更多資源
flush和fsync的區別:
- flush是把內存中的數據(包括translog和segments)都刷到磁盤
- fsync只是把translog刷新到磁盤(確保數據不丟失)。
3.4 merge
通過每隔1秒自動刷新創建新的段,用不了多久段的數量就爆炸了。
每個段文件都會消費句柄、內存、cpu資源。更重要的是,每次搜索請求都需要依次檢查每個段。段越多,查詢越慢。
ES通過后臺merge段解決這個問題。小段被合并成大段,再合并成更大的段。
3.5 刪除數據
如果刪除,其實是把數據寫到磁盤上的.del文件,然后在segemnt搜索到數據后,會在.del文件看是否有刪除標記。
在merge后會物理刪除。
4 ES搜索數據的工作原理是什么?
4.1 根據doc id 進行GET
- 協調節點根據id進行hash計算確認在哪個分片上
- 采用負載均衡的方式在primary shard和replica shard里查找數據
4.2 全文檢索
- 客戶端發送請求到協調節點node3
- node3向每個分片廣播,比如圖中廣播給了r0和p1
- 每個分片在本地執行搜索并且建立了匹配document的優先隊列(priority queue),返回document的ID和它優先隊列里的所有document的排序值給協調節點 Node 3 。
- Node3 把這些值合并到自己的優先隊列里產生全局排序結果。
5 在幾十億數據量級的場景下如何優化查詢性能?
5.1 filesystem cache
es的數據是存在磁盤上,第1次讀的時候如果沒在操作系統的filesystem cache上找到,就會先去磁盤把數據放到filesystem cache里再返回給node。所以filesystem cache要足夠大容納盡可能多的index和segment file數據,這樣就請求直接走內存,速度就快了。
另外,不必要的數據就別存在es里了,只把搜索用的字段數據放es。其他不用于檢索的數據,可以放在hbase或者mysql數據庫里。
走磁盤速度基本上都要上秒級,走內存基本上就是毫秒級了。
5.2 緩存預熱
對于熱點數據,每隔一段時間提前預熱到filesystem cache里。
5.3 冷熱分離
大量訪問很少,頻率很低的數據,單獨寫一個索引,熱數據在另一個索引中。確保filesystem cache里的熱數據不被頻繁刷掉。
5.4 document 模型設計
es里復雜的關聯語法join/nested等盡量別用,性能很低。寫入es系統之前就完成關聯,然后設計好document,添加一些field。
5.5 分頁性能優化
es的分頁性能比較差。es是分布式的,比如每頁10條數據,你要查第100頁的數據,實際上每個shard都會把自己的幾千條數據發給協調節點,然后匯總,再從匯總結果查詢到第100頁的數據。
也就是說,翻頁越深,各個shard拿到的數據越多,匯總的數據量也越多,性能越差。
1)不允許深度分頁
2)使用es的scroll api , 游標查詢會取某個時間點的快照數據。 查詢初始化之后索引上的任何變化會被它忽略。 但是沒法隨意跳到任何一頁