最新活動
包含文章發布時段最新活動,前往ES產品介紹頁,可查找ES當前活動統一入口
Elasticsearch Service自建遷移特惠政策>>
Elasticsearch Service 新用戶特惠狂歡,最低4折首購優惠 >>
Elasticsearch Service 企業首購特惠,助力企業復工復產>>
Elasticsearch是目前大數據領域最熱門的技術棧之一,騰訊雲 Elasticsearch Service(ES)是基於開源搜索引擎 Elasticsearch 打造的高可用、可伸縮的雲端全托管 Elasticsearch 服務,完善的高可用解決方案,讓業務可以放心的把重要數據存儲到騰訊雲 ES 中。了解 ES 的索引管理方法有助於揚長避短,更好的利用 ES 的強大功能,特別是當遇到性能問題時,原因通常都可回溯至數據的索引方式以及集群中的分片數量。如果未能在一開始做出最佳選擇,隨着數據量越來越大,便有可能會引發性能問題。集群中的數據越多,要糾正這一問題就越難,本文旨在幫助大家了解 ES 容量管理的方法,在一開始就管理好索引的容量,避免給后面留坑。
1. 為什么要做索引容量管理
-
在生產環境使用 ES 要面對的第一個問題通常是索引容量的規划,不合理的分片數,副本數和分片大小會對索引的性能產生直接的影響;
-
Elasticsearch 中的每個索引都由一個或多個分片組成的,每個分片都是一個 Lucene 索引實例,您可以將其視作一個獨立的搜索引擎,它能夠對 Elasticsearch 集群中的數據子集進行索引並處理相關查詢;
-
查詢和寫入的性能與索引的大小是正相關的,所以要保證高性能,一定要限制索引的大小,具體來說是限制分片數量和單個分片的大小;
-
關於分片數量,索引大小的問題這里不再贅述,可以參考 ES 官方 blog 《我在 Elasticsearch 集群內應該設置多少個分片?》(https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster)
-
直接說結論:ES 官方推薦分片的大小是 20G - 40G,最大不能超過 50G;
本文介紹 3種管理索引容量的方法,從這3種方法可以了解到 ES 管理索引容量的演進過程。
2. 方法1: 使用在索引名稱上帶上時間的方法管理索引
2.1 創建索引
索引名上帶日期的寫法:
<static_name{date_math_expr{date_format|time_zone}}>
例如
<logs-{now{yyyyMMddHH|+08:00}}-000001>
參考官方文檔:Date math support in index names(https://www.elastic.co/guide/en/elasticsearch/reference/7.x/date-math-index-names.html)
在使用的時候,索引名要 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(生命周期)是把索引定義了四個階段:
-
Hot:索引可寫入,也可查詢,也就是我們通常說的熱數據,為保證性能數據通常都是在內存中的
-
Warm:索引不可寫入,但可查詢,介於熱和冷之間,數據可以是全內存的,也可以是在 SSD 的硬盤上的
-
Cold:索引不可寫入,但很少被查詢,查詢的慢點也可接受,基本不再使用的數據,數據通常在大容量的磁盤上
-
Delete:索引可被安全的刪除
這 4個階段是 ES 定義的一個索引從生到死的過程, Hot -> Warm -> Cold -> Delete 4個階段只有 Hot 階段是必須的,其他3個階段根據業務的需求可選。
使用方法通常是下面幾個步驟:
4.1 建立 Lifecycle 策略
這一步通常在 Kibana 上操作,需要的時候再導出 ES 語句
例如下面這個策略
-
暫時只配置了 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"
-
}
-
}
-
}
-
}
⚠注意:
-
模版匹配以索引名稱 myes- 開頭的索引
-
所有使用此模版創建的索引都有一個別名 myes_reade_alias 用於方便查詢數據
-
模版綁定了上面創建的 Lifecycle 策略,並且用於 rollover 的別名是 myes_write_alias
4.3 創建索引
ES 語句:
-
PUT /myes-testindex- 000001
-
{
-
"aliases": {
-
"myes_write_alias":{}
-
}
-
}
⚠注意:
-
索引的名稱是 .*-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"
-
}
-
}
-
}
-
}
-
}
⚠注意:
-
索引使用了之前建立的索引模版
-
索引綁定了 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
-
}
-
}
-
]
-
}
⚠注意:
-
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
-
}
-
}
-
]
-
}
⚠注意:
-
如果按照這個步驟沒有發生自動 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 實際耗時情況
-
插入:~25ms
-
更新:~15ms
-
聚合:200ms 以內
參考鏈接(可復制鏈接至瀏覽器打開)
-
我在 Elasticsearch 集群內應該設置多少個分片?
(https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster)
-
使用索引生命周期管理實現熱溫冷架構
(https://www.elastic.co/cn/blog/implementing-hot-warm-cold-in-elasticsearch-with-index-lifecycle-management)
-
Index lifecycle management settings in Elasticsearchedit
(https://www.elastic.co/guide/en/elasticsearch/reference/current/ilm-settings.html)
關注“騰訊雲大數據”公眾號,技術交流、最新活動、服務專享一站Get~