Elasticsearch 索引设计实战
文章目录
索引设计的重要性
好的索引设计在整个集群规划中占据举足轻重的作用,索引的设计直接影响集群设计的好坏和复杂度。必须充分结合业务场景的时间维度和空间维度,结合业务场景充分考量增、删、改、查等全维度设计的。完全基于“设计先行,编码在后”的原则,前期会花很长时间,为的是后期工作更加顺畅,避免不必要的返工。
PB 级别的大索引如何设计
说明
单纯的普通数据索引,如果不考虑增量数据,基本上普通索引就能够满足性能要求。
我们通常的操作就是:
步骤 1:创建索引;
步骤 2:导入或者写入数据;
步骤 3:提供查询请求访问或者查询服务。
大索引的缺陷
如果每天亿万+的实时增量数据,单个索引是无法满足要求的。
存储大小限制
单个分片(Shard)实际是 Lucene 的索引,单分片能存储的最大文档数是:2,147,483,519 (= Integer.MAX_VALUE - 128)。如下命令能查看全部索引的分隔分片的文档大小:
GET _cat/shards
app_index 2 p STARTED 9443 2.8mb 127.0.0.1 Hk9wFwU
app_index 2 r UNASSIGNED
app_index 3 p STARTED 9462 2.7mb 127.0.0.1 Hk9wFwU
app_index 3 r UNASSIGNED
app_index 4 p STARTED 9520 3.5mb 127.0.0.1 Hk9wFwU
app_index 4 r UNASSIGNED
app_index 1 p STARTED 9453 2.4mb 127.0.0.1 Hk9wFwU
app_index 1 r UNASSIGNED
app_index 0 p STARTED 9365 2.3mb 127.0.0.1 Hk9wFwU
app_index 0 r UNASSIGNED
性能
当然一个索引很大的话,数据写入和查询性能都会变差。
而高效检索体现在:基于日期的检索可以直接检索对应日期的索引,无形中缩减了很大的数据规模。
比如检索:“2021-02-01”号的数据,之前的检索会是在一个月甚至更大体量的索引中进行。
现在直接检索"index_2021-02-01"的索引,效率提升好几倍。
风险
一旦一个大索引出现故障,相关的数据都会受到影响。而分成滚动索引的话,相当于做了物理隔离。
PB 级索引设计
大索引设计建议:使用模板+Rollover+Curator动态创建索引。动态索引使用效果如下:
index_2019-01-01-000001
index_2019-01-02-000002
index_2019-01-03-000003
index_2019-01-04-000004
index_2019-01-05-000005
使用模板统一配置索引
统一管理索引,相关索引字段完全一致。
使用 Rollver 增量管理索引
目的:按照日期、文档数、文档存储大小三个维度进行更新索引。使用举例:
POST /logs_write/_rollover
{
"conditions": {
"max_age": "7d",
"max_docs": 1000,
"max_size": "5gb"
}
}
索引增量更新原理
在索引模板设计阶段,模板定义一个全局别名:用途是全局检索,如图所示的别名:indexall。每次更新到新的索引后,新索引指向一个用于实时新数据写入的别名,如图所示的别名:indexlatest。同时将旧索引的别名 index_latest 移除。
别名删除和新增操作举例:
POST /_aliases
{
"actions" : [
{ "remove" : { "index" : "index_2019-01-01-000001", "alias" : "index_latest" } },
{ "add" : { "index" : "index_2019-01-02-000002", "alias" : "index_latest" } }
]
}
使用 curator 高效清理历史数据
目的:按照日期定期删除、归档历史数据。
一个大索引的数据删除方式只能使用 delete_by_query,由于 ES 中使用更新版本机制。删除索引后,由于没有物理删除,磁盘存储信息会不减反增。有同学就反馈 500GB+ 的索引 delete_by_query 导致负载增高的情况。
而按照日期划分索引后,不需要的历史数据可以做如下的处理。
删除——对应 delete 索引操作。
压缩——对应 shrink 操作。
段合并——对应 force_merge 操作。
而这一切,可以借助:curator 工具通过简单的配置文件结合定义任务 crontab 一键实现。
注意:7.X高版本借助iLM实现更为简单。
举例,一键删除 30 天前的历史数据:
[root@localhost .curator]# cat action.yml
actions:
1:
action: delete_indices
description: >-
Delete indices older than 30 days (based on index name), for logstash-
prefixed indices. Ignore the error if the filter does not result in an
actionable list of indices (ignore_empty_list) and exit cleanly.
options:
ignore_empty_list: True
disable_action: False
filters:
- filtertype: pattern
kind: prefix
value: logs_
- filtertype: age
source: name
direction: older
timestring: '%Y.%m.%d'
unit: days
unit_count: 30
如何设计分片数和副本数
分片/副本
1、分片:分片本身都是一个功能齐全且独立的“索引”,可以托管在集群中的任何节点上。
数据切分分片的主要目的:
(1)水平分割/缩放内容量 。
(2)跨分片(可能在多个节点上)分布和并行化操作,提高性能/吞吐量。
注意:分片一旦创建,不可以修改大小。
2、副本:它在分片/节点出现故障时提供高可用性。
副本的好处:因为可以在所有副本上并行执行搜索——因此扩展了搜索量/吞吐量。
注意:副本分片与主分片存储在集群中不同的节点。副本的大小可以通过:number_of_replicas动态修改。
分片和副本实战中设计
索引设置多少分片
Shard 大小官方推荐值为 20-40GB, 具体原理呢?Elasticsearch 员工 Medcl 曾经讨论如下:
Lucene 底层没有这个大小的限制,20-40GB 的这个区间范围本身就比较大,经验值有时候就是拍脑袋,不一定都好使。
Elasticsearch 对数据的隔离和迁移是以分片为单位进行的,分片太大,会加大迁移成本。
一个分片就是一个 Lucene 的库,一个 Lucene 目录里面包含很多 Segment,每个 Segment 有文档数的上限,Segment 内部的文档 ID 目前使用的是 Java 的整型,也就是 2 的 31 次方,所以能够表示的总的文档数为Integer.MAXVALUE - 128 = 2^31 - 128 = 2147483647 - 1 = 2,147,483,519,也就是21.4亿条。
同样,如果你不 forcemerge 成一个 Segment,单个 shard 的文档数能超过这个数。
单个 Lucene 越大,索引会越大,查询的操作成本自然要越高,IO 压力越大,自然会影响查询体验。
具体一个分片多少数据合适,还是需要结合实际的业务数据和实际的查询来进行测试以进行评估。
综合实战+网上各种经验分享,梳理如下:
第一步:预估一下数据量的规模。一共要存储多久的数据,每天新增多少数据?两者的乘积就是总数据量。
第二步:预估分多少个索引存储。索引的划分可以根据业务需要。
第三步:考虑和衡量可扩展性,预估需要搭建几台机器的集群。存储主要看磁盘空间,假设每台机器2TB,可用:2TB0.85(磁盘实际利用率)0.85(ES 警戒水位线)。
第四步:单分片的大小建议最大设置为 30GB。此处如果是增量索引,可以结合大索引的设计部分的实现一起规划。
前三步能得出一个索引的大小。分片数考虑维度:
1)分片数 = 索引大小/分片大小经验值 30GB 。
2)分片数建议和节点数一致。设计的时候1)、2)两者权衡考虑+rollover 动态更新索引结合。
每个 shard 大小是按照经验值 30G 到 50G,因为在这个范围内查询和写入性能较好。
索引设置多少副本
结合集群的规模,对于集群数据节点 >=2 的场景:建议副本至少设置为 1。
注意:
单节点的机器设置了副本也不会生效的。副本数的设计结合数据的安全需要。对于数据安全性要求非常高的业务场景,建议做好:增强备份(结合 ES 官方备份方案)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。