具體流程
數據先寫入內存 buffer,然后每隔 1s,將數據 refresh 到 os cache,到了 os cache 數據就能被搜索到(所以我們才說 es 從寫入到能被搜索到,中間有 1s 的延遲)。
每隔 5s,將數據寫入 translog 文件(這樣如果機器宕機,內存數據全沒,最多會有 5s 的數據丟失),translog 大到一定程度,或者默認每隔 30mins,會觸發 commit 操作,將緩沖區的數據都 flush 到 segment file 磁盤文件中。
數據寫入 segment file 之后,同時就建立好了倒排索引。
refresh
先寫入內存 buffer,在 buffer 里的時候數據是搜索不到的;同時將數據寫入 translog 日志文件。
如果 buffer 快滿了,或者到一定時間,就會將內存 buffer 數據 refresh
到一個新的 segment file
中,但是此時數據不是直接進入 segment file
磁盤文件,而是先進入 os cache
。這個過程就是 refresh
。
每隔 1 秒鍾,es 將 buffer 中的數據寫入一個新的 segment file
,每秒鍾會產生一個新的磁盤文件 segment file
,這個 segment file
中就存儲最近 1 秒內 buffer 中寫入的數據。
但是如果 buffer 里面此時沒有數據,那當然不會執行 refresh 操作,如果 buffer 里面有數據,默認 1 秒鍾執行一次 refresh 操作,刷入一個新的 segment file 中。
操作系統里面,磁盤文件其實都有一個東西,叫做
os cache
,即操作系統緩存,就是說數據寫入磁盤文件之前,會先進入os cache
,先進入操作系統級別的一個內存緩存中去。只要buffer
中的數據被 refresh 操作刷入os cache
中,這個數據就可以被搜索到了。
為什么叫 es 是准實時的?
NRT
,全稱 near real-time
。默認是每隔 1 秒 refresh 一次的,所以 es 是准實時的,因為寫入的數據 1 秒之后才能被看到。
可以通過 es 的 restful api
或者 java api
,手動執行一次 refresh 操作,就是手動將 buffer 中的數據刷入 os cache
中,讓數據立馬就可以被搜索到。
只要數據被輸入 os cache
中,buffer 就會被清空了,因為不需要保留 buffer 了,數據在 translog 里面已經持久化到磁盤去一份了。
commit 操作
重復上面的步驟,新的數據不斷進入 buffer 和 translog,不斷將 buffer
數據寫入一個又一個新的 segment file
中去,每次 refresh
完 buffer 清空,translog 保留。
隨着這個過程推進,translog 會變得越來越大。當 translog 達到一定長度的時候,就會觸發 commit
操作。
commit
(1)第一步,就是將 buffer 中現有數據 refresh
到 os cache
中去,清空 buffer。
(2)然后,將一個 commit point
寫入磁盤文件,里面標識着這個 commit point
對應的所有 segment file
,同時強行將 os cache
中目前所有的數據都 fsync
到磁盤文件中去。
(3)最后清空 現有 translog 日志文件,重啟一個 translog,此時 commit 操作完成。
這個 commit 操作叫做 flush
。默認 30 分鍾自動執行一次 flush
,但如果 translog 過大,也會觸發 flush
。
flush 操作就對應着 commit 的全過程,我們可以通過 es api,手動執行 flush 操作,手動將 os cache 中的數據 fsync 強刷到磁盤上去。
translog 日志文件的作用是什么?
你執行 commit 操作之前,數據要么是停留在 buffer 中,要么是停留在 os cache 中
無論是 buffer 還是 os cache 都是內存,一旦這台機器死了,內存中的數據就全丟了。所以需要將數據對應的操作寫入一個專門的日志文件 translog
中
一旦此時機器宕機,再次重啟的時候,es 會自動讀取 translog 日志文件中的數據,恢復到內存 buffer 和 os cache 中去。
translog 其實也是先寫入 os cache 的,默認每隔 5 秒刷一次到磁盤中去,所以默認情況下,可能有 5 秒的數據會僅僅停留在 buffer 或者 translog 文件的 os cache 中,如果此時機器掛了,會丟失 5 秒鍾的數據。但是這樣性能比較好,最多丟 5 秒的數據。也可以將 translog 設置成每次寫操作必須是直接 fsync
到磁盤,但是性能會差很多。
丟數據的問題
es 第一是准實時的,數據寫入 1 秒后可以搜索到;可能會丟失數據的。
有 5 秒的數據,停留在 buffer、translog os cache、segment file os cache 中,而不在磁盤上,此時如果宕機,會導致 5 秒的數據丟失。
刪除/更新數據底層原理
刪除操作,commit 的時候會生成一個 .del
文件,里面將某個 doc 標識為 deleted
狀態,那么搜索的時候根據 .del
文件就知道這個 doc 是否被刪除了。
更新操作,就是將原來的 doc 標識為 deleted
狀態,然后新寫入一條數據.
Es更新和刪除文檔的過程
刪除和更新也都是寫操作,但是Elasticsearch中的文檔是不可變的,因此不能被刪除或者改動以展示其變更;
刪除操作,磁盤上的每個段都有一個相應的.del文件。當刪除請求發送后,文檔並沒有真的被刪除,而是在.del文件中被標記為刪除。該文檔依然能匹配查詢,但是會在結果中被過濾掉。當段合並時,在.del文件中被標記為刪除的文檔將不會被寫入新段。
更新操作
在新的文檔被創建時,Elasticsearch會為該文檔指定一個版本號
當執行更新時,舊版本的文檔在.del文件中被標記為刪除,新版本的文檔被索引到一個新段。舊版本的文檔依然能匹配查詢,但是會在結果中被過濾掉。
merge操作
buffer 每 refresh 一次,就會產生一個 segment file
,所以默認情況下是 1 秒鍾一個 segment file
,這樣下來 segment file
會越來越多,此時會定期執行 merge。
每次 merge 的時候
(1)會將多個 segment file
合並成一個
(2)同時這里會將標識為 deleted
的 doc 給物理刪除掉
(3)然后將新的 segment file
寫入磁盤
這里會寫一個 commit point
,標識所有新的 segment file
,然后打開 segment file
供搜索使用,同時刪除舊的 segment file
。
Master選舉
Elasticsearch的選主是ZenDiscovery模塊負責的,主要包含
- Ping(節點之間通過這個RPC來發現彼此)
- Unicast(單播模塊包含一個主機列表以控制哪些節點需要ping通)這兩部分;
(1)對所有可以成為master的節點,根據nodeId字典排序。每次選舉每個節點都把自己所知道節點排一次序,然后選出第一個節點作為master節點。
(2)如果對某個節點的投票數達到一定的值(可以成為master節點數n/2+1)並且該節點自己也選舉自己,那這個節點就是master。否則重新選舉一直到滿足上述條件。
補充:master節點的職責主要包括集群、節點和索引的管理,不負責文檔級別的管理;data節點可以關閉http功能。
20個節點,10個選了一個master,另外10個選了一個master,怎么辦
當集群master候選數量不小於3個時,可以通過設置最少投票通過數量,超過所有候選節點一半以上來解決腦裂問題;
當候選數量為兩個時,只能修改為唯一的一個master候選,其他作為data節點,避免腦裂問題。
對於大數據量的聚合如何實現
Elasticsearch 提供的首個近似聚合是cardinality 度量。它提供一個字段的基數,即該字段的distinct或者unique值的數目。它是基於HLL算法的。HLL 會先對我們的輸入作哈希運算,然后根據哈希運算的結果中的 bits 做概率估算從而得到基數。其特點是:可配置的精度,用來控制內存的使用(更精確 = 更多內存);小的數據集精度是非常高的;我們可以通過配置參數,來設置去重需要的固定內存使用量。無論數千還是數十億的唯一值,內存使用量只與你配置的精確度相關 .
在並發情況下,Es如果保證讀寫一致
可以通過版本號使用樂觀並發控制,以確保新版本不會被舊版本覆蓋,由應用層來處理具體的沖突;
對於寫操作,一致性級別支持quorum/one/all,默認為quorum
即只有當大多數分片可用時才允許寫操作。但即使大多數可用,也可能存在因為網絡等原因導致寫入副本失敗,這樣該副本被認為故障,分片將會在一個不同的節點上重建。
對於讀操作
可以設置replication為sync(默認),這使得操作在主分片和副本分片都完成后才會返回;
如果設置replication為async時,也可以通過設置搜索請求參數_preference為primary來查詢主分片,確保文檔是最新版本。