這篇文章主要介紹Elasticsearch的索引工作機制,它是如何利用translog來保證數據的安全,以及我們在生產環境中如何優化translog的參數來最大化性能,主要會介紹到elastic中常見的2個操作:refresh和flush,以及這2個接口是如何保證數據能夠被檢索到的。
數據持久化
我們把數據寫到磁盤后,還要調用fsync才能把數據刷到磁盤中,如果不這樣做在系統掉電的時候就會導致數據丟失,這個原理相信大家都清楚,elasticsearch為了高可靠性必須把所有的修改持久化到磁盤中。
elastic底層采用的是lucene這個庫來實現倒排索引的功能,在lucene的概念里每一條記錄稱為document(文檔),lucene使用segment(分段)來存儲數據,用commit point來記錄所有segment的元數據,一條記錄要被搜索到,必須寫入到segment中,這一點非常重要,后面會介紹為什么elastic搜索是near-realtime(接近實時的)而不是實時的。
elastic使用translog來記錄所有的操作,我們稱之為write-ahead-log,我們新增了一條記錄時,es會把數據寫到translog和in-memory buffer(內存緩存區)中,如下圖所示:
內存緩存區和translog就是near-realtime的關鍵所在,前面我們講過新增的索引必須寫入到segment后才能被搜索到,因此我們把數據寫入到內存緩沖區之后並不能被搜索到,如果希望該文檔能立刻被搜索,需要手動調用refresh操作。
refresh操作
默認情況下,es每隔一秒鍾執行一次refresh,可以通過參數index.refresh_interval
來修改這個刷新間隔,執行refresh操作具體做了哪些事情呢?
- 所有在內存緩沖區中的文檔被寫入到一個新的segment中,但是沒有調用fsync,因此內存中的數據可能丟失
- segment被打開使得里面的文檔能夠被搜索到
- 清空內存緩沖區
執行refresh后的狀態如下圖所示:
refresh的開銷比較大,我在自己環境上測試10W條記錄的場景下refresh一次大概要14ms,因此在批量構建索引時可以把refresh間隔設置成-1來臨時關閉refresh,等到索引都提交完成之后再打開refresh,可以通過如下接口修改這個參數:
1 |
curl -XPUT 'localhost:9200/test/_settings' -d '{ |
另外當你在做批量索引時,可以考慮把副本數設置成0,因為document從主分片(primary shard)復制到從分片(replica shard)時,從分片也要執行相同的分析、索引和合並過程,這樣的開銷比較大,你可以在構建索引之后再開啟副本,這樣只需要把數據從主分片拷貝到從分片:
1 |
curl -XPUT 'localhost:9200/my_index/_settings' -d ' { |
執行完批量索引之后,把刷新間隔改回來:
1 |
curl -XPUT 'localhost:9200/my_index/_settings' -d '{ |
你還可以強制執行一次refresh以及索引分段的合並:
1 |
curl -XPOST 'localhost:9200/my_index/_refresh' |
flush操作
隨着translog文件越來越大時要考慮把內存中的數據刷新到磁盤中,這個過程稱為flush,flush過程主要做了如下操作:
- 把所有在內存緩沖區中的文檔寫入到一個新的segment中
- 清空內存緩沖區
- 往磁盤里寫入commit point信息
- 文件系統的page cache(segments) fsync到磁盤
- 刪除舊的translog文件,因此此時內存中的segments已經寫入到磁盤中,就不需要translog來保障數據安全了
flush之后的狀態如下所示:
es有幾個條件來決定是否flush到磁盤,不同版本的es參數有所不同,大家可以參考es對應版本的文檔來查看這幾個參數:es translog,這里介紹下1.7版本的flush參數:
- index.translog.flush_threshold_ops,執行多少次操作后執行一次flush,默認無限制
- index.translog.flush_threshold_size,translog的大小超過這個參數后flush,默認512mb
- index.translog.flush_threshold_period,多長時間強制flush一次,默認30m
- index.translog.interval,es多久去檢測一次translog是否滿足flush條件
上面的參數是es多久執行一次flush操作,在系統恢復過程中es會比較translog和segments中的數據來保證數據的完整性,為了數據安全es默認每隔5秒鍾會把translog刷新(fsync)到磁盤中,也就是說系統掉電的情況下es最多會丟失5秒鍾的數據,如果你對數據安全比較敏感,可以把這個間隔減小或者改為每次請求之后都把translog fsync到磁盤,但是會占用更多資源;這個間隔是通過下面2個參數來控制的:
- index.translog.sync_interval 控制translog多久fsync到磁盤,最小為100ms
- index.translog.durability translog是每5秒鍾刷新一次還是每次請求都fsync,這個參數有2個取值:request(每次請求都執行fsync,es要等translog fsync到磁盤后才會返回成功)和async(默認值,translog每隔5秒鍾fsync一次)
讀者需要弄清楚flush和fsync的區別,flush是把內存中的數據(包括translog和segments)都刷到磁盤,而fsync只是把translog刷新的磁盤(確保數據不丟失)。