ES 18 - (底層原理) Elasticsearch寫入索引數據的過程 以及優化寫入過程

来源:https://www.cnblogs.com/shoufeng/archive/2019/04/18/10729751.html
-Advertisement-
Play Games

Elasticsearch是如何通過Lucene把索引數據寫入磁碟的? 為了實現更快的實時性、更可靠的數據持久化, 以及更高效的大量segment文件的歸併, 還能不能優化這個過程? 本片文章介紹一些優化實踐, 歡迎交流呀( ⊙ o ⊙ ) ...


目錄

1 Lucene操作document的流程

Lucene將index數據分為segment(段)進行存儲和管理.

Lucene中, 倒排索引一旦被創建就不可改變, 要添加或修改文檔, 就需要重建整個倒排索引, 這就對一個index所能包含的數據量, 或index可以被更新的頻率造成了很大的限制.

為了在保留不變性的前提下實現倒排索引的更新, Lucene引入了一個新思路: 使用更多的索引, 也就是通過增加新的補充索引來反映最新的修改, 而不是直接重寫整個倒排索引.

—— 這樣就能確保, 從最早的版本開始, 每一個倒排索引都會被查詢到, 查詢完之後再對結果進行合併.

1.1 添加document的流程

① 將數據寫入buffer(記憶體緩衝區);

② 執行commit操作: buffer空間被占滿, 其中的數據將作為新的 index segment 被commit到文件系統的cache(緩存)中;

③ cache中的index segment通過fsync強制flush到系統的磁碟上;

④ 寫入磁碟的所有segment將被記錄到commit point(提交點)中, 並寫入磁碟;

④ 新的index segment被打開, 以備外部檢索使用;

⑤ 清空當前buffer緩衝區, 等待接收新的文檔.

說明:

(a) fsync是一個Unix系統調用函數, 用來將記憶體緩衝區buffer中的數據存儲到文件系統. 這裡作了優化, 是指將文件緩存cache中的所有segment刷新到磁碟的操作.

(b) 每個Shard都有一個提交點(commit point), 其中保存了當前Shard成功寫入磁碟的所有segment.

1.2 刪除document的流程

① 提交刪除操作, 先查詢要刪除的文檔所屬的segment;

② commit point中包含一個.del文件, 記錄哪些segment中的哪些document被標記為deleted了;

③ 當.del文件中存儲的文檔足夠多時, ES將執行物理刪除操作, 徹底清除這些文檔.

  • 在刪除過程中進行搜索操作:

    依次查詢所有的segment, 取得結果後, 再根據.del文件, 過濾掉標記為deleted的文檔, 然後返回搜索結果. —— 也就是被標記為delete的文檔, 依然可以被查詢到.

  • 在刪除過程中進行更新操作:

    將舊文檔標記為deleted, 然後將新的文檔寫入新的index segment中. 執行查詢請求時, 可能會匹配到舊版本的文檔, 但由於.del文件的存在, 不恰當的文檔將被過濾掉.


2 優化寫入流程 - 實現近實時搜索

2.1 流程的改進思路

(1) 現有流程的問題:

插入的新文檔必須等待fsync操作將segment強制寫入磁碟後, 才可以提供搜索.而 fsync操作的代價很大, 使得搜索不夠實時.

(2) 改進寫入流程:

① 將數據寫入buffer(記憶體緩衝區);

② 不等buffer空間被占滿, 而是每隔一定時間(預設1s), 其中的數據就作為新的index segment被commit到文件系統的cache(緩存)中;

③ index segment 一旦被寫入cache(緩存), 就立即打開該segment供搜索使用;

④ 清空當前buffer緩衝區, 等待接收新的文檔.

—— 這裡移除了fsync操作, 便於後續流程的優化.

優化的地方: 過程②和過程③:

segment進入操作系統的緩存中就可以提供搜索, 這個寫入和打開新segment的輕量過程被稱為refresh.

2.2 設置refresh的間隔

Elasticsearch中, 每個Shard每秒都會自動refresh一次, 所以ES是近實時的, 數據插入到可以被搜索的間隔預設是1秒.

(1) 手動refresh —— 測試時使用, 正式生產中請減少使用:

# 刷新所有索引:
POST _refresh
# 刷新某一個索引: 
POST employee/_refresh

(2) 手動設置refresh間隔 —— 若要優化索引速度, 而不註重實時性, 可以降低刷新頻率:

# 創建索引時設置, 間隔1分鐘: 
PUT employee
{
    "settings": {
        "refresh_interval": "1m"
    }
}
# 在已有索引中設置, 間隔10秒: 
PUT employee/_settings
{
    "refresh_interval": "10s"
}

(3) 當你在生產環境中建立一個大的新索引時, 可以先關閉自動刷新, 要開始使用該索引時再改回來:

# 關閉自動刷新: 
PUT employee/_settings
{
    "refresh_interval": -1 
} 
# 開啟每秒刷新: 
PUT employee/_settings
{
    "refresh_interval": "1s"
} 


3 優化寫入流程 - 實現持久化變更

Elasticsearch通過事務日誌(translog)來防止數據的丟失 —— durability持久化.

3.1 文檔持久化到磁碟的流程

① 索引數據在寫入記憶體buffer(緩衝區)的同時, 也寫入到translog日誌文件中;

② 每隔refresh_interval的時間就執行一次refresh:

(a) 將buffer中的數據作為新的 index segment, 刷到文件系統的cache(緩存)中;

(b) index segment一旦被寫入文件cache(緩存), 就立即打開該segment供搜索使用;

③ 清空當前記憶體buffer(緩衝區), 等待接收新的文檔;

④ 重覆①~③, translog文件中的數據不斷增加;

每隔一定時間(預設30分鐘), 或者當translog文件達到一定大小時, 發生flush操作, 並執行一次全量提交:

(a) 將此時記憶體buffer(緩衝區)中的所有數據寫入一個新的segment, 並commit到文件系統的cache中;

(b) 打開這個新的segment, 供搜索使用;

(c) 清空當前的記憶體buffer(緩衝區);

(d) 將translog文件中的所有segment通過fsync強制刷到磁碟上;

(e) 將此次寫入磁碟的所有segment記錄到commit point中, 並寫入磁碟;

(f) 刪除當前translog, 創建新的translog接收下一波創建請求.

擴展: translog也可以被用來提供實時CRUD.

當通過id查詢、更新、刪除一個文檔時, 從segment中檢索之前, 先檢查translog中的最新變化 —— ES總是能夠實時地獲取到文檔的最新版本.

共計:3599 個字

3.2 基於translog和commit point的數據恢復

(1) 關於translog的配置:

flush操作 = 將translog中的記錄刷到磁碟上 + 更新commit point信息 + 清空translog文件.

Elasticsearch預設: 每隔30分鐘就flush一次;
或者: 當translog文件的大小達到上限(預設為512MB)時主動觸發flush.

相關配置為:

# 發生多少次操作(累計多少條數據)後進行一次flush, 預設是unlimited: 
index.translog.flush_threshold_ops

# 當translog的大小達到此預設值時, 執行一次flush操作, 預設是512MB: 
index.translog.flush_threshold_size

# 每隔多長時間執行一次flush操作, 預設是30min:
index.translog.flush_threshold_period

# 檢查translog、並執行一次flush操作的間隔. 預設是5s: ES會在5-10s之間進行一次操作: 
index.translog.interval

(2) 數據的故障恢復:

① 增刪改操作成功的標誌: segment被成功刷新到Primary Shard和其對應的Replica Shard的磁碟上, 對應的操作才算成功.

translog文件中存儲了上一次flush(即上一個commit point)到當前時間的所有數據的變更記錄. —— 即translog中存儲的是還沒有被刷到磁碟的所有最新變更記錄.

③ ES發生故障, 或重啟ES時, 將根據磁碟中的commit point去載入已經寫入磁碟的segment, 並重做translog文件中的所有操作, 從而保證數據的一致性.

(3) 非同步刷新translog:

為了保證不丟失數據, 就要保護translog文件的安全:

Elasticsearch 2.0之後, 每次寫請求(如index、delete、update、bulk等)完成時, 都會觸發fsync將translog中的segment刷到磁碟, 然後才會返回200 OK的響應;

或者: 預設每隔5s就將translog中的數據通過fsync強制刷新到磁碟.

—— 提高數據安全性的同時, 降低了一點性能.

==> 頻繁地執行fsync操作, 可能會產生阻塞導致部分操作耗時較久. 如果允許部分數據丟失, 可設置非同步刷新translog來提高效率.

PUT employee/_settings
{
    "index.translog.durability": "async",
    "index.translog.sync_interval": "5s"
}


4 優化寫入流程 - 實現海量segment文件的歸併

4.1 存在的問題

由上述近實時性搜索的描述, 可知ES預設每秒都會產生一個新的segment文件, 而每次搜索時都要遍歷所有的segment, 這非常影響搜索性能.

為解決這一問題, ES會對這些零散的segment進行merge(歸併)操作, 儘量讓索引中只保有少量的、體積較大的segment文件.

這個過程由獨立的merge線程負責, 不會影響新segment的產生.

同時, 在merge段文件(segment)的過程中, 被標記為deleted的document也會被徹底物理刪除.

4.2 merge操作的流程

① 選擇一些有相似大小的segment, merge成一個大的segment;
② 將新的segment刷新到磁碟上;
③ 更新commit文件: 寫一個新的commit point, 包括了新的segment, 並刪除舊的segment;
④ 打開新的segment, 完成搜索請求的轉移;
⑤ 刪除舊的小segment.

4.3 優化merge的配置項

segment的歸併是一個非常消耗系統CPU和磁碟IO資源的任務, 所以ES對歸併線程提供了限速機制, 確保這個任務不會過分影響到其他任務.

(1) 歸併線程的速度限制:

限速配置 indices.store.throttle.max_bytes_per_sec的預設值是20MB, 這對寫入量較大、磁碟轉速較高的伺服器來說明顯過低.

對ELK Stack應用, 建議將其調大到100MB或更高. 可以通過API設置, 也可以寫在配置文件中:

PUT _cluster/settings
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "100mb"
    }
}
// 響應結果如下: 
{
    "acknowledged": true,
    "persistent": {
        "indices": {
            "store": {
                "throttle": {
                    "max_bytes_per_sec": "100mb"
                }
            }
        }
    },
    "transient": {}
}

(2) 歸併線程的數目:

推薦設置為CPU核心數的一半, 如果磁碟性能較差, 可以適當降低配置, 避免發生磁碟IO堵塞:

PUT employee/_settings
{
    "index.merge.scheduler.max_thread_count" : 8
}

(3) 其他策略:

# 優先歸併小於此值的segment, 預設是2MB:
index.merge.policy.floor_segment

# 一次最多歸併多少個segment, 預設是10個: 
index.merge.policy.max_merge_at_once

# 一次直接歸併多少個segment, 預設是30個
index.merge.policy.max_merge_at_once_explicit

# 大於此值的segment不參與歸併, 預設是5GB. optimize操作不受影響
index.merge.policy.max_merged_segment

4.4 optimize介面的使用

segment的預設大小是5GB, 在非常龐大的索引中, 仍然會存在很多segment, 這對文件句柄、記憶體等資源都是很大的浪費.

但由於歸併任務非常消耗資源, 所以一般不會選擇加大 index.merge.policy.max_merged_segment 配置, 而是在負載較低的時間段, 通過optimize介面強制歸併segment:

# 強制將segment歸併為1個大的segment: 
POST employee/_optimize?max_num_segments=1

# 在終端中的操作方法: 
curl -XPOST http://ip:5601/employee/_optimize?max_num_segments=1

optimize線程不會受到任何資源上的限制, 所以不建議對還在寫入數據的熱索引(動態索引)執行這個操作.

實戰建議: 對一些很少發生變化的老索引, 如日誌信息, 可以將每個Shard下的segment合併為一個單獨的segment, 節約資源, 還能提高搜索效率.

參考資料

Elasticsearch 基礎理論 & 配置調優

https://www.elastic.co/guide/cn/elasticsearch/guide/current/making-text-searchable.html

https://www.elastic.co/guide/cn/elasticsearch/guide/current/near-real-time.html

https://www.elastic.co/guide/cn/elasticsearch/guide/current/translog.html

https://www.elastic.co/guide/cn/elasticsearch/guide/current/merge-process.html

版權聲明

作者: 馬瘦風

出處: 博客園 馬瘦風的博客

感謝閱讀, 如果文章有幫助或啟發到你, 點個[

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 轉載 longctw 版權聲明:只為分享、歡迎轉載^V^ https://blog.csdn.net/xulong_08/article/details/81463054 Linux是目前應用最廣泛的伺服器操作系統,基於Unix,開源免費,由於系統的穩定性和安全性,市場占有率很高,幾乎成為程式代碼運 ...
  • Linux關機命令用poweroff。 halt停止系統運行,但不關閉電源,坑啊! ...
  • . uname 顯示系統相關信息,如內核版本號,硬體架構 -a # 顯示系統所有相關信息 -m # 顯示電腦硬體架構 -n # 顯示主機名稱 -r # 顯示內核發行版本號 -s # 顯示內核名稱 -v # 顯示內核版本 -p # 顯示主機處理器類型 -o # 顯示操作系統名稱 -l # 顯示硬體平 ...
  • Step 1 . 準備工作 關閉防火牆; 安裝 MySQL依賴於libaio 庫;下載,解壓,重命名 MySQL的執行文件;新建用戶組和用戶 等準備工作在此不再贅述。 可執行文件目錄為 /data/mysql57 添加用戶和組的指令 groupadd mysql useradd mysql -g m ...
  • 1.分散式鎖介紹 在電腦系統中,鎖作為一種控制併發的機制無處不在。 單機環境下,操作系統能夠在進程或線程之間通過本地的鎖來控制併發程式的行為。而在如今的大型複雜系統中,通常採用的是分散式架構提供服務。 分散式環境下,基於本地單機的鎖無法控制分散式系統中分開部署客戶端的併發行為,此時分散式鎖就應運而 ...
  • 筆記記錄自林曉斌(丁奇)老師的《MySQL實戰45講》 (本篇內圖片均來自丁奇老師的講解,如有侵權,請聯繫我刪除) 18) --為什麼這些SQL語句邏輯相同,性能卻差異巨大? 本篇我們以三個例子來記錄。 案例一:條件欄位函數操作 一個交易系統中有這樣一個交易記錄表,假設現在已經記錄了從16年年初到1 ...
  • 目錄 Windows下安裝與配置 下載 安裝 驗證安裝 配置服務 測試 Linux下安裝與配置 準備工作 安裝 驗證與測試 Macox下安裝與配置 準備工作 安裝 驗證與測試 下載 安裝 驗證安裝 配置服務 測試 準備工作 安裝 驗證與測試 準備工作 安裝 驗證與測試 Redis 在 Windows ...
  • 在SQL Server中,我們可以使用sp_name這個系統存儲過程重命名資料庫中對象的名稱。 此對象可以是表、 索引、 列、 別名,約束等數據類型(具體可以參考官方文檔)。上周在使用這個函數重構資料庫中約束的時候,遇到了下麵錯誤,如下所示: USE AdventureWorks2014; GO s... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...