Elasticsearch 索引容量管理实践
作者:gavinyao,騰訊 PCG 后臺開發工程師
Elasticsearch 是目前大數據領域最熱門的技術棧之一,騰訊云 Elasticsearch Service(ES)是基于開源搜索引擎 Elasticsearch 打造的高可用、可伸縮的云端全托管 Elasticsearch 服務,完善的高可用解決方案,讓業務可以放心的把重要數據存儲到騰訊云 ES 中。
了解 ES 的索引管理方法有助于揚長避短,更好的利用 ES 的強大功能,特別是當遇到性能問題時,原因通常都可回溯至數據的索引方式以及集群中的分片數量。如果未能在一開始做出最佳選擇,隨著數據量越來越大,便有可能會引發性能問題。集群中的數據越多,要糾正這一問題就越難,本文旨在幫助大家了解 ES 容量管理的方法,在一開始就管理好索引的容量,避免給后面留坑。
1. 為什么要做索引容量管理
在生產環境使用 ES 要面對的第一個問題通常是索引容量的規劃,不合理的分片數,副本數和分片大小會對索引的性能產生直接的影響;
Elasticsearch 中的每個索引都由一個或多個分片組成的,每個分片都是一個 Lucene 索引實例,您可以將其視作一個獨立的搜索引擎,它能夠對 Elasticsearch 集群中的數據子集進行索引并處理相關查詢;
查詢和寫入的性能與索引的大小是正相關的,所以要保證高性能,一定要限制索引的大小,具體來說是限制分片數量和單個分片的大小;
關于分片數量,索引大小的問題這里不再贅述,可以參考 ES 官方 blog 《我在 Elasticsearch 集群內應該設置多少個分片?》;
直接說結論:ES 官方推薦分片的大小是 20G - 40G,最大不能超過 50G。
本文介紹 3 種管理索引容量的方法,從這 3 種方法可以了解到 ES 管理索引容量的演進過程:
2. 方法 1: 使用在索引名稱上帶上時間的方法管理索引
2.1 創建索引
索引名上帶日期的寫法:
<static_name{date_math_expr{date_format|time_zone}}>日期格式就是 java 的日期格式:
yyyy:年 MM:月 dd:日 hh:1~12小時制(1-12) HH:24小時制(0-23) mm:分 ss:秒 S:毫秒 E:星期幾 D:一年中的第幾天 F:一月中的第幾個星期(會把這個月總共過的天數除以7) w:一年中的第幾個星期 W:一月中的第幾星期(會根據實際情況來算) a:上下午標識 k:和HH差不多,表示一天24小時制(1-24)。 K:和hh差不多,表示一天12小時制(0-11)。 z:表示時區參考官方文檔:Date math support in index names。
例如:
<logs-{now{yyyyMMddHH|+08:00}}-000001>在使用的時候,索引名要 urlencode 后再使用:
PUT /%3Cmylogs-%7Bnow%7ByyyyMMddHH%7C%2B08%3A00%7D%7D-000001%3E{ "aliases": { "mylogs-read-alias": {} }}執行結果:
{"acknowledged"?:?true,"shards_acknowledged"?:?true,"index"?:?"mylogs-2020061518-000001" }2.2 寫入數據
寫入數據的時候也要帶上日期:
POST?/%3Cmylogs-%7Bnow%7ByyyyMMddHH%7C%2B08%3A00%7D%7D-000001%3E/_doc {"name":"xxx"}執行結果:
{"_index"?:?"mylogs-2020061518-000001","_type"?:?"_doc","_id"?:?"VNZut3IBgpLCCHbxDzDB","_version"?:?1,"result"?:?"created","_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1 }2.3 查詢數據
由于數據分布在多個索引里,查詢的時候要在符合條件的所有索引查詢,可以使用下面的方法查詢。
2.3.1 使用逗號分割指定多個索引
GET?/mylogs-2020061518-000001,mylogs-2020061519-000001/_search {"query":{"match_all":{}}}2.3.2 使用通配符查詢
GET?/mylogs-*/_search {"query":?{"match_all":?{}} }執行結果:
{"took"?:?0,"timed_out"?:?false,"_shards"?:?{"total"?:?1,"successful"?:?1,"skipped"?:?0,"failed"?:?0},"hits"?:?{"total"?:?{"value"?:?1,"relation"?:?"eq"},"max_score"?:?1.0,"hits"?:?[{"_index"?:?"mylogs-2020061518-000001","_type"?:?"_doc","_id"?:?"VNZut3IBgpLCCHbxDzDB","_score"?:?1.0,"_source"?:?{"name"?:?"xxx"}}]} }2.3.3 使用別名查詢
GET?/mylogs-read-alias/_search {"query":?{"match_all":?{}} }執行結果同上。
2.4 使用帶日期的索引名稱的缺陷
這個方法的優點是比較直觀能夠通過索引名稱直接分辨出數據的新舊,缺點是:
不是所有數據都適合使用時間分割,對于寫入之后還有修改的數據不適合;
直接使用時間分割也可能存在某段時間數據量集中,導致索引分片超過設計容量的問題,從而影響性能;
為了解決上述問題還需要配合 rollover 策略使用,索引的維護比較復雜。
3. 方法 2: 使用 Rollover 管理索引
Rollover 的原理是使用一個別名指向真正的索引,當指向的索引滿足一定條件(文檔數或時間或索引大小)更新實際指向的索引。
3.1 創建索引并且設置別名
注意: 索引名稱的格式為 {.*}-d 這種格式的,數字默認是 6 位:
PUT?myro-000001 {"aliases":?{"myro_write_alias":{}} }3.2 通過別名寫數據
使用 bulk 一次寫入了 3 條記錄:
POST?/myro_write_alias/_bulk?refresh=true {"create":{}} {"name":"xxx"} {"create":{}} {"name":"xxx"} {"create":{}} {"name":"xxx"}執行結果:
{"took"?:?37,"errors"?:?false,"items"?:?[{"create"?:?{"_index"?:?"myro-000001","_type"?:?"_doc","_id"?:?"wVvFtnIBUTVfQxRWwXyM","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1,"status"?:?201}},{"create"?:?{"_index"?:?"myro-000001","_type"?:?"_doc","_id"?:?"wlvFtnIBUTVfQxRWwXyM","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?1,"_primary_term"?:?1,"status"?:?201}},{"create"?:?{"_index"?:?"myro-000001","_type"?:?"_doc","_id"?:?"w1vFtnIBUTVfQxRWwXyM","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?2,"_primary_term"?:?1,"status"?:?201}}] }記錄都寫到了 myro-000001 索引下。
3.3 執行 rollover 操作
rollover 的 3 個條件是并列關系,任意一個條件滿足就會發生 rollover:
POST?/myro_write_alias/_rollover {"conditions":?{"max_age":???"7d","max_docs":??3,"max_size":?"5gb"} }執行結果:
{"acknowledged"?:?true,"shards_acknowledged"?:?true,"old_index"?:?"myro-000001","new_index"?:?"myro-000002","rolled_over"?:?true,"dry_run"?:?false,"conditions"?:?{"[max_docs:?3]"?:?true,"[max_size:?5gb]"?:?false,"[max_age:?7d]"?:?false} }分析一下執行結果:
?"new_index"?:?"myro-000002""[max_docs:?3]"?:?true,從結果看出滿足了條件("[max_docs: 3]" : true)發生了 rollover,新的索引指向了 myro-000002。
再寫入一條記錄:
POST?/myro_write_alias/_doc {"name":"xxx"}已經寫入了新的索引,結果符合預期:
{"_index"?:?"myro-000002","_type"?:?"_doc","_id"?:?"BdbMtnIBgpLCCHbxhihi","_version"?:?1,"result"?:?"created","_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1 }3.4 使用 Rollover 的缺點
必須明確執行了 rollover 指令才會更新 rollover 的別名對應的索引;
通常可以在寫入數據之后 再執行一下 rollover 命令,或者采用配置系統 cron 腳本的方式;
增加了使用的 rollover 的成本,對于開發者來說不夠自動化。
4. 方法 3: 使用 ILM(Index Lifecycle Management ) 管理索引
ES 一直在索引管理這塊進行優化迭代,從 6.7 版本推出了索引生命周期管理(Index Lifecycle Management ,簡稱 ILM)機制,是目前官方提供的比較完善的索引管理方法。所謂 Lifecycle(生命周期)是把索引定義了四個階段:
lifecycleHot:索引可寫入,也可查詢,也就是我們通常說的熱數據,為保證性能數據通常都是在內存中的;
Warm:索引不可寫入,但可查詢,介于熱和冷之間,數據可以是全內存的,也可以是在 SSD 的硬盤上的;
Cold:索引不可寫入,但很少被查詢,查詢的慢點也可接受,基本不再使用的數據,數據通常在大容量的磁盤上;
Delete:索引可被安全的刪除。
這 4 個階段是 ES 定義的一個索引從生到死的過程, Hot -> Warm -> Cold -> Delete 4 個階段只有 Hot 階段是必須的,其他 3 個階段根據業務的需求可選。
使用方法通常是下面幾個步驟:
4.1 建立 Lifecycle 策略
這一步通常在 Kibana 上操作,需要的時候再導出 ES 語句,例如下面這個策略:
在 Kibina 中創建 lifecycle 策略暫時只配置了 Hot 階段;
為了方便驗證,最大文檔數(max_docs) 超過 2 個時就 rollover。
導出的語句如下:
PUT?_ilm/policy/myes-lifecycle {"policy":?{"phases":?{"hot":?{"min_age":?"0ms","actions":?{"rollover":?{"max_age":?"30d","max_size":?"50gb","max_docs":?2},"set_priority":?{"priority":?100}}}}} }4.2 建立索引模版
ES 語句如下:
PUT?/_template/myes_template {"index_patterns":?["myes-*"],"aliases":?{"myes_reade_alias":?{}},"settings":?{"index":?{"lifecycle":?{"name":?"myes-lifecycle","rollover_alias":?"myes_write_alias"},"refresh_interval":?"30s","number_of_shards":?"12","number_of_replicas":?"1"}},"mappings":?{"properties":?{"name":?{"type":?"keyword"}}} }:warning:注意:
模版匹配以索引名稱 myes- 開頭的索引;
所有使用此模版創建的索引都有一個別名 myes_reade_alias 用于方便查詢數據;
模版綁定了上面創建的 Lifecycle 策略,并且用于 rollover 的別名是 myes_write_alias。
4.3 創建索引
ES 語句:
PUT?/myes-testindex-000001 {"aliases":?{"myes_write_alias":{}} }:warning:注意:
索引的名稱是 .*-d 的形式;
索引的別名用于 lifecycle 做 rollover。
4.4 查看索引配置
GET?/myes-testindex-000001 {}執行結果:
{"myes-testindex-000001"?:?{"aliases"?:?{"myes_reade_alias"?:?{?},"myes_write_alias"?:?{?}},"mappings"?:?{"dynamic_templates"?:?[{"message_full"?:?{"match"?:?"message_full","mapping"?:?{"fields"?:?{"keyword"?:?{"ignore_above"?:?2048,"type"?:?"keyword"}},"type"?:?"text"}}},{"message"?:?{"match"?:?"message","mapping"?:?{"type"?:?"text"}}},{"strings"?:?{"match_mapping_type"?:?"string","mapping"?:?{"type"?:?"keyword"}}}],"properties"?:?{"name"?:?{"type"?:?"keyword"}}},"settings"?:?{"index"?:?{"lifecycle"?:?{"name"?:?"myes-lifecycle","rollover_alias"?:?"myes_write_alias"},"refresh_interval"?:?"30s","number_of_shards"?:?"12","translog"?:?{"sync_interval"?:?"5s","durability"?:?"async"},"provided_name"?:?"myes-testindex-000001","max_result_window"?:?"65536","creation_date"?:?"1592222799955","unassigned"?:?{"node_left"?:?{"delayed_timeout"?:?"5m"}},"priority"?:?"100","number_of_replicas"?:?"1","uuid"?:?"tPwDbkuvRjKtRHiL4fKcPA","version"?:?{"created"?:?"7050199"}}}} }:warning:注意:
索引使用了之前建立的索引模版;
索引綁定了 lifecycle 策略并且寫入別名是 myes_write_alias。
4.5 寫入數據
POST?/myes_write_alias/_bulk?refresh=true {"create":{}} {"name":"xxx"} {"create":{}} {"name":"xxx"} {"create":{}} {"name":"xxx"}執行結果:
{"took"?:?18,"errors"?:?false,"items"?:?[{"create"?:?{"_index"?:?"myes-testindex-000001","_type"?:?"_doc","_id"?:?"jF3it3IBUTVfQxRW1Xys","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1,"status"?:?201}},{"create"?:?{"_index"?:?"myes-testindex-000001","_type"?:?"_doc","_id"?:?"jV3it3IBUTVfQxRW1Xys","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1,"status"?:?201}},{"create"?:?{"_index"?:?"myes-testindex-000001","_type"?:?"_doc","_id"?:?"jl3it3IBUTVfQxRW1Xys","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1,"status"?:?201}}] }:warning:注意:
3 條記錄都寫到了 myes-testindex-000001 中, Lifecycle 策略明明設置的是 2 條記錄就 rollover 為什么會三條都寫到同一個索引了呢?
再次執行上面的語句,寫入 3 條記錄發現新的數據都寫到了 myes-testindex-000002 中, 結果符合預期。
{"took"?:?17,"errors"?:?false,"items"?:?[{"create"?:?{"_index"?:?"myes-testindex-000002","_type"?:?"_doc","_id"?:?"yl0JuHIBUTVfQxRWvsv5","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1,"status"?:?201}},{"create"?:?{"_index"?:?"myes-testindex-000002","_type"?:?"_doc","_id"?:?"y10JuHIBUTVfQxRWvsv5","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1,"status"?:?201}},{"create"?:?{"_index"?:?"myes-testindex-000002","_type"?:?"_doc","_id"?:?"zF0JuHIBUTVfQxRWvsv5","_version"?:?1,"result"?:?"created","forced_refresh"?:?true,"_shards"?:?{"total"?:?2,"successful"?:?2,"failed"?:?0},"_seq_no"?:?0,"_primary_term"?:?1,"status"?:?201}}] }:warning:注意:如果按照這個步驟沒有發生自動 rollover 數據仍然寫到了 myes-testindex-000001 中,需要 配置 Lifecycle 自動 Rollover 的時間間隔, 參考下文。
4.6 配置 Lifecycle 自動 Rollover 的時間間隔
由于 ES 是一個準實時系統,很多操作都不能實時生效;
Lifecycle 的 rollover 之所以不用每次手動執行 rollover 操作是因為 ES 會隔一段時間判斷一次索引是否滿足 rollover 的條件;
ES 檢測 ILM 策略的時間默認為 10min。
修改 Lifecycle 配置:
PUT?_cluster/settings {"transient":?{"indices.lifecycle.poll_interval":?"3s"} }5. ES 在 QQ 家校群作業統計功能上的實踐
疫情期間線上教學需求爆發,QQ 的家校群功能也迎來了一批發展紅利,家校群的作業功能可以輕松在 QQ 群里實現作業布置,提交,批改等功能,深受師生們的喜愛。
5.1 使用場景簡介
近期推出的作業統計功能,可以指定時間段+指定科目動態給出排名,有效提高了學生答題的積極性。在功能的實現上如果用傳統的 SQL + KV 的方式實現成本比較高,要做到高性能也需要花不少精力,借助 ES 強大的統計聚合能力大大降低了開發成本,實現了需求的快速上線。
5.2 申請資源
ES 版本:7.5.1
高級特性:騰訊云 ES 白金版
單節點容量:1000GB
節點數:3
總容量:3000GB
5.3 索引使用方案
按群尾號 % 100 把數據分為 100 個索引
每個索引 12 個分片
每 40000W(120GB)發生一次 Rollover
單個分片最大大小 10GB
5.4 實際耗時情況
插入:~ 25ms
更新:~ 15ms
聚合:200ms 以內
參考鏈接
我在 Elasticsearch 集群內應該設置多少個分片?
Elasticsearch rollover index 滾動索引
使用索引生命周期管理實現熱溫冷架構
Index lifecycle management settings in Elasticsearchedit
總結
以上是生活随笔為你收集整理的Elasticsearch 索引容量管理实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 「递归」第7集 | 腾讯开源联盟出征!
- 下一篇: 万级K8s集群背后etcd稳定性及性能优