引言
在現代分布式搜索引擎Elasticsearch中,文檔的索引、更新和刪除操作不僅是用戶交互的核心入口,更是底層存儲架構設計的關鍵挑戰。本文圍繞以下核心鏈路展開:
- 文檔生命周期管理:從客戶端請求路由到分片定位,從內存緩沖區(Buffer)到事務日志(Translog)的雙重寫入機制,揭示數據持久化的完整路徑;
- 實時性與可靠性平衡:通過剖析Translog同步/異步刷盤策略、內存緩沖區刷新(Refresh)與持久化刷盤(Flush)的觸發邏輯,解讀搜索可見性與故障恢復的底層保障;
- Lucene段合并優化:深入對比分層合并(TieredMergePolicy)、字節大小合并(LogByteSizeMergePolicy)和文檔數量合并(LogDocMergePolicy)等策略,探討如何通過段合并提升查詢效率、釋放磁盤空間并優化I/O負載。
通過系統性梳理,本文將為開發者提供從API操作到底層存儲的全視角技術圖譜,助力高性能搜索服務的設計與調優。
索引文檔的過程
索引文檔:將新的文檔添加到索引中或者覆蓋已經存在的文檔。
并非只有協調節點可能進行數據轉發,可以將請求發送到任何一個數據節點,該節點都可以處理請求或將請求轉發給適當的節點以完成請求處理。
- 客戶端向 Node 1 (任意一個節點)發送新建、覆蓋請求。
- 節點使用文檔ID(文檔ID可以人工指定,不指定將自動創建唯一值) 確定文檔屬于分片 0(hash(_id)%number_of_primary_shards) 。請求會被轉發到 Node 3,因為分片 0 的主分片目前被分配在 Node 3 上。
- Node 3 寫入操作不僅保存在內存緩沖區中,同時也被記錄到事務日志(Translog)中。Translog是一個位于磁盤上的追加日志,它記錄了所有對索引的更改,以確保在發生故障時能夠恢復數據。
- 當內存緩沖區達到一定大小,或者Translog達到一定大小時。Flush操作會將內存緩沖區中的數據以及Translog中的更改持久化到磁盤上的Lucene索引文件的Segment中,并且會清空舊的Translog。
number_of_primary_shards:索引的主分片數量。
更新和刪除文檔的過程
- 客戶端向任意節點發送更新、刪除請求,協調節點并根據文檔ID確定要更新的分片(Shard),將請求轉發到分片的主節點上。
- 主分片會創建一個新的文檔,保留相同的文檔ID和一個更高的版本號。同時在段對應的.del文件中記錄舊版本的文檔。(更新文檔)
- 在段對應的.del文件中記錄舊版本的文檔。(刪除文檔)
- 修改操作不僅保存在內存緩沖區中,同時也被記錄到事務日志(Translog)中。Translog是一個位于磁盤上的追加日志,它記錄了所有對索引的更改,以確保在發生故障時能夠恢復數據。
- 當內存緩沖區達到一定大小,或者Translog達到一定大小時。Flush操作會將內存緩沖區中的數據以及Translog中的更改持久化到磁盤上的Lucene索引文件的Segment中,并且會清空舊的Translog。
- 廢棄的文檔由后臺線程在段合并階段進行刪除,釋放磁盤空間。
Translog刷盤(Flush)時機
Translog的刷盤:是指將Translog內存中的數據寫入到Translog日志中(磁盤)。
Translog的刷盤方式有兩種:同步(request)和異步(async),index.translog.durability為request表示同步(默認同步),為async表示異步。
同步方式:意味著每次寫操作之后會立即將 Translog 刷新到磁盤。
異步方式:可以通過index.translog.sync_interval(默認5s),當達到配置值時觸發刷盤。
Lucene中的段(Segment)
Segment是物理日志,而TransLog是邏輯日志,在Lucene中,每當有新的文檔被添加時,數據首先寫入內存緩沖區(buffer)。當緩沖區達到一定大小或滿足特定條件時,數據會被刷新到磁盤,形成一個新的段。這個初始段的大小依賴于緩沖區的大小和寫入的文檔數量。多個索引的修改都會被分開寫入多個段中。
Lucene中的段生成
只有生成Luence段之后,才能被搜索到。
refresh操作:index.refresh_interval(默認1s),可以適當調大例如30s。定時將內存緩沖區數據寫入到新的Lucene段文件中,不會清空translog。
flush操作:當translog大小達到index.translog.flush_threshold_size(默認512m),會將translog中的數據寫入到磁盤上的 Lucene 段文件中,并創建一個新的 translog 文件,并清空舊的translog。
Lucene中的段合并
段合并的好處
- 提高查詢效率:多個小段可能導致查詢時需要訪問多個索引文件,使查詢效率降低。合并段可以減少段的數量,從而減少查詢過程中需要讀取的文件,提高查詢速度。
- 釋放磁盤空間:刪除文檔不會立即從段中移除,而是標記為已刪除。通過段合并,可以徹底清除這些標記為刪除的文檔,釋放磁盤空間。
段合并策略
TieredMergePolicy(分層合并策略)
默認段合并策略,根據段的大小和數量將段分為不同的層級(Tiers),并在合適的時機觸發段合并。
通過設置segments_per_tier
參數控制每層的最大段數,每層段數超過時觸發合并。通過設置max_merge_at_once
參數控制一次合并的最大段數。
通過段的大小對段進行分層。具體來說,它會將段按照大小分為不同的層,每一層中的段大小范圍不同。層的劃分并不是固定的,而是動態調整的。
LogByteSizeMergePolicy(基于字節大小的合并策略)
基于段的字節大小來決定合并。它會嘗試將小段合并成較大的段,以控制合并后的段大小。
通過設置min_merge_size
參數控制段合并操作的最小段大小(小于min_merge_size
優先合并)。max_merge_size
參數控制段合并操作的最大段大小,當段的大小超過這個閾值時,不再參與合并。
LogDocMergePolicy(基于文檔數量的合并策略)
基于段中的文檔數量來決定合并。它會嘗試將包含少量文檔的段合并成包含更多文檔的段。
通過設置min_merge_docs
參數控制段合并操作的最小段的文檔數量(小于min_merge_docs
優先合并)。和max_merge_docs
參數控制段合并操作的最大段的文檔數量,當段的文檔數量超過這個閾值時,不再參與合并。
感謝您的閱讀!如果文章中有任何問題或不足之處,歡迎及時指出,您的反饋將幫助我不斷改進與完善。期待與您共同探討技術,共同進步!