在今天的文章里,我們來主要介紹一下Elasticsearch的refresh及flush兩種操作的區別。如果我們從字面的意思上講,好像都是刷新的意思。但是在Elasticsearch中,這兩種操作是有非常大的區別的。本指南將有效解決兩者之間的差異。 我們還將介紹Lucene功能的基礎知識,例如重新打開(reopen)和提交(commit),這有助於理解refresh和flush操作。
Refresh及Flush
乍一看,Refresh和Flush操作的通用目的似乎是相同的。 兩者都用於使文檔在索引操作后立即可供搜索。 在Elasticsearch中添加新文檔時,我們可以對索引調用_refresh或_flush操作,以使新文檔可用於搜索。 要了解這些操作的工作方式,您必須熟悉Lucene中的Segments,Reopen和Commits。Apache Lucene是Elasticsearch中的基礎查詢引擎。
Lucene中的Segments
在Elasticsearch中,最基本的數據存儲單位是shard。 但是,通過Lucene鏡頭看,情況會有所不同。 在這里,每個Elasticsearch分片都是一個Lucene索引(index),每個Lucene索引都包含幾個Lucene segments。 一個Segment包含映射到文檔里的所有術語(terms)一個反向索引(inverted index)。
下圖顯示了段的概念及其如何應用於Elasticsearch索引及其分片:
這種分Segment的概念是,每當創建新文檔時,它們就會被寫入新的Segment中。 每當創建新文檔時,它們都屬於一個新的Segment,並且無需修改前一個Segment。 如果必須刪除文檔,則在其原始Segment中將其標記為已刪除。 這意味着它永遠不會從Segement中物理刪除。
與更新相同:文檔的先前版本在上一個Segment中被標記為已刪除,更新后的版本保留在當前Segment中的同一文檔ID下。
Lucene中的Reopen
當調用Lucene Reopen時,將使累積的數據可用於搜索。 盡管可以搜索最新數據,但這不能保證數據的持久性或未將其寫入磁盤。 我們可以調用n次重新打開功能,並使最新數據可搜索,但不能確定磁盤上是否存在數據。
Lucene中的Commits
Lucene提交使數據安全。 對於每次提交,來自不同段的數據將合並並推送到磁盤,從而使數據持久化。 盡管提交是持久保存數據的理想方法,但問題是每個提交操作都占用大量資源。 每個提交操作都有其自己的內部 I/O 操作以及與其相關的讀/寫周期。 這就是為什么我們希望在基於Lucene的系統中一次又一次地重新使用重新打開功能以使新數據可搜索的確切原因。
Elasticsearch中的Translog
Elasticsearch采用另一種方法來解決持久性問題。 它在每個分片中引入一個事務日志(transaction log)。 已建立索引的新文檔將傳遞到此事務日志和內存緩沖區中。 下圖顯示了此過程:
Elasticsearch中的refresh
當我們把一條數據寫入到Elasticsearch中后,它並不能馬上被用於搜索。新增的索引必須寫入到Segment后才能被搜索到,因此我們把數據寫入到內存緩沖區之后並不能被搜索到。新增了一條記錄時,Elasticsearch會把數據寫到translog和in-memory buffer(內存緩存區)中,如下圖所示:
如果希望該文檔能立刻被搜索,需要手動調用refresh操作。在Elasticsearch中,默認情況下_refresh操作設置為每秒執行一次。 在此操作期間,內存中緩沖區的內容將復制到內存中新創建的Segment中,如下圖所示。 結果,新數據可用於搜索。
這個refresh的時間間隔可以由index設置中index.refresh_interval來定義。執行完refresh后的結果如下:
我們可以看出來,在In-meomory buffer中,現在所有的東西都是空的,但是Translog里還是有東西的。
refresh的開銷比較大,我在自己環境上測試10W條記錄的場景下refresh一次大概要14ms,因此在批量構建索引時可以把refresh間隔設置成-1來臨時關閉refresh,等到索引都提交完成之后再打開refresh,可以通過如下接口修改這個參數:
curl -XPUT 'localhost:9200/test/_settings' -d '{
"index" : {
"refresh_interval" : "-1"
}
}'
另外當你在做批量索引時,可以考慮把副本數設置成0,因為document從主分片(primary shard)復制到從分片(replica shard)時,從分片也要執行相同的分析、索引和合並過程,這樣的開銷比較大,你可以在構建索引之后再開啟副本,這樣只需要把數據從主分片拷貝到從分片:
curl -XPUT 'localhost:9200/my_index/_settings' -d ' {
"index" : {
"number_of_replicas" : 0
}
}'
執行完批量索引之后,把刷新間隔改回來:
curl -XPUT 'localhost:9200/my_index/_settings' -d '{
"index" : {
"refresh_interval" : "1s"
}
}'
你還可以強制執行一次refresh以及索引分段的合並:
curl -XPOST 'localhost:9200/my_index/_refresh'
curl -XPOST 'localhost:9200/my_index/_forcemerge?max_num_segments=5'
Translog及持久化存儲
但是,translog如何解決持久性問題? 每個Shard中都存在一個translog,這意味着它與物理磁盤內存有關。 它是同步且安全的,因此即使對於尚未提交的文檔,您也可以獲得持久性和持久性。 如果發生問題,可以還原事務日志。 同樣,在每個設置的時間間隔內,或在成功完成請求(索引,批量,刪除或更新)后,將事務日志提交到磁盤。
Elasticsearch中的Flush
Flush實質上意味着將內存緩沖區中的所有文檔都寫入新的Lucene Segment,如下面的圖所示。 這些連同所有現有的內存段一起被提交到磁盤,該磁盤清除事務日志(參見圖4)。 此提交本質上是Lucene提交(commit)。
Flush會定期觸發,也可以在Translog達到特定大小時觸發。 這些設置可以防止Lucene提交帶來的不必要的費用。
結論
在本指南中,我們探索了兩個緊密相關的Elasticsearch操作,_flush和_refresh顯示了它們之間的共性和差異。 我們還介紹了Lucene的基礎架構組件-重新打開(reopen)並提交(commits)-這有助於掌握Elasticsearch中_refresh和_flush操作的要點。
簡而言之,_refresh用於使新文檔可見以進行搜索。 而_flush用於將內存中的段保留在硬盤上。 _flush
不會影響Elasticsearch中文檔的可見性,因為搜索是在內存段中進行的,而不是_refresh
會影響其可見性。
參考:
【1】https://qbox.io/blog/refresh-flush-operations-elasticsearch-guide
【2】https://www.ezlippi.com/blog/2018/04/elasticsearch-translog.html