万象在Elasticsearch 存储和检索上的优化
1. 问题是什么?
ES 单机 JVM 内存社区建议不超过 32G;额,实际上单机版的情况下,万象能够分给 ES 的 JVM 内存在 8-16G,但面临的查询总数据量是几亿或几十亿,会因复杂的聚合查询频繁造成内存熔断以致使 ES 服务不可用;师傅!怎么实现资源有限的情况还能确保业务支撑?。
2. 从什么方向考虑实现?
在大数据的分析优化中,根本上有俩方面的优化
- 减少数据读取大小(分布式计算中常用列式存储+压缩+序列化的方式来实现)。
- 减少网络 shuffle(网络混洗:在分布式的计算中,查询结果需要把所有机器的数据通过网络拉取到一台机器上做汇总运算,网络拉取的数据量越少,整体计算时间越短)。
定方向:我们要基于减少数据读取的方向来优化ES查询,在大数据其他组件中主要以(列式存储+压缩+序列化)的组合形式;对应到 ES 中特性,无法直接应用列式存储和热查询数据的压缩。
在 ES 的日常使用中,我们通常使用 ES Rollover 特性通过定时脚本触发条件,以索引大小或自然时间来滚动生成索引,统一使用一个别名 index_name 来查询该别名下所有的索引数据,这样通过别名查询全部索引的形式在提供方便的同时也伴随着一次查询读取数据量过大的问题。
那么,想读取 2022 年 11 月 30 日上午 0:00 到 2:00 的数据,直接去'index_name-20221130'的索引去查数据不就 OK 了吗?但这样新的问题就来了;
- 如果以自然时间来定时触发滚动新索引,你在 00:01 分触发滚动条件,那其实有 00:00 - 00:01 分的数据是写入到昨天的索引的,如果你在 00:00 分触发写数据也会有延迟的数据不会计入到新的索引中。
- 如果以索引大小或者文档个数来当触发条件,不是每天都能满足条件滚动出以当天日期来做索引名字的索引。
3.怎么解决?
3.1. 解决数据准确的问题
我们可以使用数据中的事件时间来确定,数据应该被写入到哪个索引中。
索引名称解读 index_name-20221128-03-01
- ’index_name‘ 代表索引名称前缀,可以和索引别名相同,为模板的前缀。
- '20221128' 代表事件时间所在的自然日。
- '03' 代表时区位,如果默认设置 24 小时分 3 区,则 [0,8),[8,16),[16,24) ,如果事件时间是 17:50:00 小时位为17落到区间 [16,24),则对应的索引名称第三段时区段的值为 03,单机版的情况,为了防止 shard 个数太多,可以设置时间分区默认为 1,这样,该位一直为 01。
- '01'代表时区内的滚动次数,如果设置滚动条件为索引大小 30G,文档个数 5000 万,如果达到任意条件会自动的滚动到该时区的下一个索引 index_name-20221128-03-02。
3.2. 写入-覆盖 ES 本身 Rollover 特性
curl -u elastic:'password' localhost:9200/_cat/indices?format=json
{
"health": "green",
"status": "open",
"index": "securitylog-20220518-01-01",
"uuid": "KiHhGGKHSI-Seox1IMcdZA",
"pri": "2",
"rep": "0",
"docs.count": "10693950",
"docs.deleted": "0",
"store.size": "13gb",
"pri.store.size": "13gb"
}
3.3. 检索-根据简单算法减少数据读取量
根据索引名称解读部分中的说明,我们可以尝试检索不同的索引数据范围(一次ES查询请求,请求索引可以通过罗列的形式请求多个索引 indexA-*,indexB-*):
- 检索时间点 2022 年 11 月 28 日 20:00:00,最近 5 分钟,最近 1 小时,最近 2 小时,最近 6 小时,均可以根据当前时间所在的时区来选择索引范围,使用索引范围 index_name-20221128-03-01。
- 检索时间点 2022 年 11 月 28 日 20:00:00,最近 12 小时,使用索引范围 index_name-20221128-03-*, index_name-20221128-02-*。
- 检索时间点 2022 年 11 月 28 日 20:00:00,最近 24 小时,使用索引范围 index_name-20221128-03-*, index_name-20221128-02-*,index_name-20221128-01-*,index_name-20221127-03-*。
- 检索时间点 2022 年 11 月 28 日 20:00:00,最近 3 天,使用索引范围 index_name-20221128-*, index_name-20221127-*, index_name-20221126-*。
- 检索时间点 2022 年 11 月 28 日 20:00:00,本月,可以列举出最近 30 天的所有 30 个索引,也可以使用 index_name-2022112*。
3.4. 支持更广的数据维度查询
3.4.1 进阶版本的索引名称定义
- 'Index_name' 代表索引名称前缀,可以和索引别名相同,为模板的前缀。
- '2022' 代表事件时间所在的年份。
- ’04‘ 代表事件时间在第几季度。
- ’11‘ 代表事件时间是几月。
- ’48‘ 代表事件时间是这一年的第多少周。
- ’28‘ 代表事件时间是在 11 月的 28 日。
- ’03‘ 代表一天中的时区位,如果默认设置 24 小时分 3 区,则 [0,8),[8,16),[16,24) 。
- '01' 代表时区内的滚动次数位,如果设置滚动条件为索引大小 30G,文档个数 5000 万,如果达到任意条件会自动的滚动到该时区的下一个索引 index_name-20221128-03-02。
3.4.2 进阶查询新增使用维度:
季度:检索时间点 2022 年 11 月 28 日 20:00:00,当季度的数据查询,使用索引范围 index_name-2022#04*
周:检索时间点 2022 年 11 月 28 日 20:00:00,本周数据查询,使用索引范围 index_name-2022#04#11#48*
最近一个月:检索时间点 2022 年 11 月 28 日 20:00:00,最近一个月的查询范围,使用索引范围 index*name-2022#04#11#48*, indexname-2022#04#11#47**, index_name-2022#04#11#46*, index_name-2022#04#11#45*
4.优缺点
优点:
- 支持一天接入数百 G 的数据同时,可以以更少的资源快速响应查询数据。
- 在写入时,可以按照事件时间写入数据即使实时过来几天之前的数据,也能自动将历史数据写入过去对应的索引中,不会出现数据写入错乱导致的查询数据不准确问题。
- 在写入时,可以根据日期,索引大小和索引文档数,自动滚动生成新的索引。
缺点:
- 索引滚动次数多,高效率的 shard 个数范围在 500-1000 个,导致单节点的情况,ES 保存周期有限。
万象团队 李世钰