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