引入ELK前需要知道的“坑”(上)

看了就要关注我哦!!

徐磊,去哪儿网平台事业部大数据中心开发工程师,2015年加入Qunar,负责实时日志相关的开发与运维工作,有多年的电信,云计算从业经验。

去哪儿网内部从2014年开始接触并逐步推广应用 ELK 技术栈,解决诸如日志检索,数据分析,运营后台等诸多业务。如今公司内部运行着超过100个 Elasticsearch 集群,其中最大的集群超过400个节点,数据量超过1.3PB。每天通过 Logstash 处理的数据量几十亿,大部分开发人员都已经在 Kibana 上检索着应用日志,分析问题。经过几年的使用和运维 ELK,我们也积累了许多经验,也在社区中帮助许多人解答了他们的疑问,发现许多人在刚接触 ELK 的时候,总会有一些共性问题,借着这个公众号的渠道,总结分享出来,让更多的人在引入 ELK 时少走一些弯路。

Elasticsearch篇

适用场景

这应该是所有刚接入 Elasticsearch 的人问的最多的问题,Elasticsearch 究竟能在我的系统里承担什么样的功能。我们要明确一个前提,Elasticsearch 是以检索+数据统计起家的,现在逐步向商业 BI 的能力靠近,所以其发展路线图就决定了不适合某些场景,比如拿 Elasticsearch 当成一个 schemaless的数据库用,还要让其提供交易系统中的事物隔离,这显然就是错用的一个典型场景。Elasticsearch 的 NRT 特性就无法保证数据写入立即可见,同时多个 doc 级联操作没有任何源生的原子性保障,更不用说默认写入时最终一致性。但是如果用 Elasticsearch 作为一个数据库 binlog 的备份,支撑后台运营系统的多维度查询,检索等需求,那这是 Elasticsearch 最擅长的地方。所以引入 Elasticsearch 前一定要先考虑其优势和劣势,再决定上线。

Index vs Type

许多初次接触 Elasticsearch 的人,都会听到或者搜到这样一个信息,Elasticsearch 中的一些概念,是可以和关系型数据库映射的,我大致整理了下,如下图:

这里要明确指出的一个问题是,Elasticsearch 中的 Index/Type 在实现上,并非是关系型数据中的 Database Schema/Table 强隔离。如果按照关系型数据库的设计来反推 Elasticsearch,可能会导致严重的后果,在此列举几个:(1) 数据无法写入(MapperParsingExcep-tion);(2) 检索结果乱序(_score);(3) 低版本Elasticsearch数据膨胀(LUCENE-6863)。深究其根本原因,Type 并非 Lucene 源生提供的,而是 Elasticsearch 自己在逻辑层实现了 Type,对文档默认插入 _type 字段,这实际上对初学者产生了概念上的误导。

而一个好消息是,从 Elasticsearch 6.0/5.5 开始,将尽量屏蔽多 Type 带来的不稳定因素,7.0彻底移除 Type,所以如果你正在使用最新版本的 Elasticsearch,那么这个坑可以顺利绕过。针对还在使用 1.x/2.x/5.x 的系统,由此衍生的一个常见问题就是——“我应该如何设计索引”,这个问题在各大论坛和讨论组经常被讨论,结合我们的实践,发现这个问题的答案很简单,遵守两条规则即可:(1)Index 用来对数据分类;(2)Type 用来对数据分区。这么说可能比较抽象,我们以订单数据举个例子,比如整个 Qunar 的订单数据要存放到 Elasticsearch 中,那么比较推荐以下做法(单Type):(1)/ordercenter-2018.11.18/data/(2)/ordercenter-2018.11.19/data/(3)/ordercenter-2018.11.20/data/这样巨量的数据以天跨度分区,单个索引的数据量可以降低到一个可控的范围内,对后续的数据迁移和配置调整都留出了自由度,而且针对归档数据进行 close 或 snapshot 都可以独立进行,把影响隔离到最低。如果每天订单数据量很小,那么也可以考虑合并成月/周索引,配合 Type 查询:(1)/ordercenter-2018.11/18/(2)/ordercenter-2018.11/19/(3)/order_center-2018.11/20/和第一种方法相比,索引的实践跨度增加了,许多变更都要等到下个月/周才能生效,对运维不利,而且归档周期拉长。

Document ID的选择

5.x 及以上版本且写入量大的集群,强烈推荐使用自生成文档 ID,这样可以避免 shard 内的唯一性检查,尤其是数据量越打越大的情况下,这对吞吐量来说是很大的提升。如果出于业务的需要,需要手工设置文档 ID,那么更推荐同长度,带补齐的二进制数据,比如考虑左补零的自增数字,带来更高查询效率:

索引多 vs 数据多

还有一个问题就是一个 Elasticsearch 集群中,究竟可以支持多少个索引。这个结论我很难给一个量化的结果,毕竟不同的系统的索引情况都是不一样的,要具体情况具体分析,但是这里将给出一个分析的方法,尽早避免未来索引规模上涨带来的各种不稳定因素。首先,索引数量变大,将直接影响 Master 节点的内存使用量,严重时会导致 Master 节点的持续 FGC,Cluster 级别的操作将大量超过,这个情况在生产系统是不可接受的。但是这并非一个充分必要条件,有一种情况也可以导致 Master 节点的 FGC,即单个 doc 的字段过多或嵌套层级太深。其次,如果索引数量上涨带来了数据量的上涨,那么也将同时影响 Data 节点的内存,每个 Shard 都会加载倒排索引信息到内存,并且这部分内存是无法被 GC 掉的。一个长期有数据写入,但不做数据归档的集群,如果突然有一天发现 Data 节点开始持续的 GC 了,就要考虑下 Segment 是否占用了过多内存,然后做相应的处理。讲到这里,其实分析方法已经很清晰了,看两个维度即可,索引数量和集群数据量,前者影响 Master 节点,后者影响 Data 节点。

有了这两个分析角度,我们就可以对集群的实际情况做出分析,并对容量做一些预估,提前考虑扩容。(1)控制 doc 的字段数量,尽量降低嵌套层级,提高集群可承载的索引数量上限,同时利用 JVM heap 监控反推容量,设置报警线。(2)以时间粒度切分索引,定期针对历史索引 optimize(force_merge),减少索引文件碎片,此操作也能降低 segment 内存占用,如果能对索引进行 close 并归档,那效果更好。(3)在前两步的基础上,优化 doc,例如非查询字段设置成 index:no,非检索类数据 norms:false 等,降低存储占用,优化内存。(4)参考历史监控数据,整理内存使用量与总数据量的比例关系,为扩容提供分析支持。

大磁盘 vs 大内存

有了上一节的信息,我们就可以继续推算 Elasticsearch 的机器硬件参数,如机器的内存与磁盘配比,以目前 Qunar 的情况为例,最大的集群存储1TB数据大约消耗0.9GB左右的内存(这个比例请依据自己的实际情况计算),在这个配比下,假设你的机器数据盘15TB,那么极端情况下,这些数据大约占用13.5GB的堆内存,那单个 JVM 实例配合 -Xmx30g -Xms30g 即可搞定,并留了一部分内存用作 Cache/IndexBuffer/Bulk 等操作使用。那么机器的内存推荐64GB以上即可。如果改用大容量磁盘,单机超过50TB的存储,那么通过以上的条件的简单计算,就发现30g堆内存的 JVM 已经无法搞定了,此时要么考虑单机双实例,要么采取更大的堆内存支撑这些数据,机器的内存响应也要扩大到128GB以上。当然,如果公司有矿,全部清一色的256/512GB的内存+大容量SSD卡,可以暂时忽略以上的推算过程。

单盘 vs 多盘

如果有人准备从1.x升级到2.x,或者新引入5.x的 Elasticsearch,那么都会面临一个问题,机器应该使用分盘存储的方式,还是做个 raid 合并成一块盘存储数据。这个问题的根源其实是2.x针对数据完成性的一个优化,同一个 shard 的数据保证放到一个盘。如果你的集群采取了集中式的索引管理,比如每天/每周/每月集中创建索引,那么剩余空间最大的磁盘就会被强制分配一批 shard(此时新索引数据量为0),待数据灌入时,会发现这个磁盘IO暴涨,最后影响整个集群的吞吐。所以从2.x开始,集中管理式的集群+提前创建索引的运维策略下,就不推荐继续使用分盘存储的方式。

对于1.x升级到2.x的人来说,这就是个悲剧了,不但索引要重新构建,raid 都要重做了,这个就坑大了。 6.x是否重新针对此情况做了优化,我还没有看源码,并不确定,这个问题就留给各位读者自行验证了。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181218G094FV00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券