Elasticsearch 的更新操作(如 `_update` 和 `_update_by_query`)在底層實現上有一些復雜的原理,這些原理涉及到 Elasticsearch 的數據存儲機制、索引機制以及事務日志(Translog)的使用。以下是 Elasticsearch 更新操作的主要原理:
1. 文檔的存儲和索引
在 Elasticsearch 中,文檔是以 JSON 格式存儲的,并且每個文檔都有一個唯一的 `_id`。這些文檔被存儲在索引中,索引由多個分片(Shards)組成,每個分片是一個獨立的 Lucene 索引。
2. 更新操作的基本流程
當執行更新操作時,Elasticsearch 會按照以下步驟處理:
a. 查找文檔
- Elasticsearch 首先根據提供的 `_id` 在索引中查找目標文檔。
- 如果文檔不存在,更新操作的行為取決于是否設置了 `doc_as_upsert` 參數:
? - 如果設置了 `doc_as_upsert`,則會插入一個新的文檔。
? - 如果沒有設置 `doc_as_upsert`,則更新操作會失敗。
b. 讀取舊文檔
- 如果文檔存在,Elasticsearch 會讀取舊文檔的內容。
c. 應用更新
- Elasticsearch 將更新內容(如通過 `_update` API 提供的 `doc` 部分)應用到舊文檔上。
- 如果是部分更新(如 `_update`),Elasticsearch 會將新的字段值合并到舊文檔中。
- 如果是全量替換(如 `PUT`),Elasticsearch 會用新的 JSON 數據完全替換舊文檔。
d. 寫入新文檔
- 更新后的文檔會被重新索引,生成一個新的 Lucene segment。
- 舊文檔會被標記為刪除(但不會立即從磁盤中刪除)。
e. 事務日志(Translog)
- 更新操作會被記錄在事務日志(Translog)中,以確保操作的持久性。
- 事務日志是一個持久化的日志文件,用于記錄所有對索引的寫操作(包括插入、更新和刪除)。
f. 刷新和提交
- 當事務日志達到一定大小或時間間隔時,Elasticsearch 會執行 `_flush` 操作:
? - 將內存中的數據和事務日志中的操作持久化到磁盤。
? - 清空事務日志。
- 在 `_flush` 操作之前,Elasticsearch 會先執行 `_refresh` 操作,確保數據在文件系統緩存中可用,從而可以被搜索到。
3. 并發控制
Elasticsearch 使用樂觀并發控制機制來處理并發更新:
- 每個文檔都有一個序列號(`seq_no`)和一個主版本號(`primary_term`)。
- 在更新操作時,可以指定 `if_seq_no` 和 `if_primary_term` 參數,以確保只有在文檔的序列號和主版本號匹配時才執行更新。
- 如果序列號或主版本號不匹配,更新操作會失敗,從而避免覆蓋其他用戶的更改。
4. 批量更新
對于批量更新操作(如 `_update_by_query`),Elasticsearch 會按照以下步驟處理:
- 執行查詢,找到需要更新的文檔集合。
- 對每個文檔應用更新邏輯。
- 逐個更新文檔,每個文檔的更新過程與單個文檔更新類似。
- `_update_by_query` 操作會使用內部的重試機制來處理并發沖突。
5. 性能優化
- 內存管理:Elasticsearch 會將更新操作的結果暫存到內存中,以提高性能。
- 批量處理:對于批量更新操作,Elasticsearch 會盡量減少對磁盤的寫入次數,通過批量處理來優化性能。
- 異步處理:某些更新操作(如 `_flush` 和 `_refresh`)是異步執行的,以減少對請求的阻塞。
總結
Elasticsearch 的更新操作涉及多個步驟,包括查找文檔、讀取舊文檔、應用更新、寫入新文檔、記錄事務日志、刷新和提交等。通過這些機制,Elasticsearch 確保了更新操作的高效性、一致性和持久性。同時,通過樂觀并發控制機制,Elasticsearch 能夠有效處理并發更新,避免數據沖突。