陈曦:性能与稳定并存 Elasticsearch 调优实践


本文地址:http://www.6aiq.com/article/1571732516452
知乎专栏 点击关注
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出

转载自 云加社区 2018-11-02

本文整理自作者在云加社区线下沙龙活动中的精彩分享,主题内容 Elassticsearch 调优实践。

以下为分享内容:

我今天分享的是 Elassticsearch 调优实践,首先自我介绍一下,我资历比较浅,我是腾讯 TEG 基础架构部后台开发工程师,虽然我不是项目经理,但是我们项目负责人就在下面,我还是很有压力的。

今天分享一下使用 ES 的经验以及我们对 ES 内核的改动,在此之前介绍一下 ES 的功能,首先来看 ES 的基础概念,这部分介绍一下 Elastic Stack。在场有多少用过 ES 的?还是挺多的,ES 首先是一个搜索引擎,我总结了一下特点,高性能,它是通过 Lucene 实现搜索引擎,在此之上增加了分布式集群。可扩展,系统支持多分片存储,高并发写入、查询。第二是高可靠。第三是易于管理,节点可以弹性伸缩。第四是易使用,具有很强大的综合分析能力,比如说分桶聚合,桶内的运算都可以支持,甚至通过脚本进行复杂的运算。

Elastic Stack 提供数据采集、清洗、存储、可视化完整解决方案。Beats 负责数据采集,Logstash 负责数据清洗转发,ES 本身就是存储和搜索,第四个 Kibana 就是把你的数据作可视化展示,是比较完整的工具链。ES 有比较广泛的应用领域,对内部和外部系统都有广泛的使用。主要有日志分析是一个场景,再有数据分析统计,还有搜索,全文检索都可以做的。功能比较强大。

我们再来看一下 Elassticsearch 的基础概念 - 数据模型,第一张图把 ES 数据模型概念和传统数据库做了对比。ES 本身是 schema less 的,有比较特殊的字段需要通过 Mapping 设置一下,每个数据点就是一行数据 Document,ES 数据分类通过 Index 这层完成的。这是逻辑的概念,我们再来看物理实际存储的架构,首先分为两层,一个是 ES,下面是 Lucene,一个 ES 集群有多个 node 组成的。一个 ES 实例会承载一些 Shard,多个 shard 会落到不同机器上面,P1 和 P2 是两个不同的 Shard,并且 shard 有主从的概念,对于 ES 每个 Shard 落地下来是一个 Lucene Index。

Lucene 基于节点宕掉的考虑会写 Translog,每次写入会产生这个 Translog,对你的每次操作都会产生 Disk 的 IO,对 Lucene 有三个不同的过程,第一个是 Refresh,先缓存内存里面,过一段时间,默认情况是每秒一次,把它整理成一个 Lucene 的 Segment,当一秒钟一次刷成 Refresh 的时候数据就可以搜索了。第二就是 Flush,这个时候会把 Lucene 内存结构实际刷盘下去。再有一个就是第三个过程是 Merge,把一些小的 segment 不断合成大的 segment。

第二部分就是 Elassticsearch 的调优,通用的方案,这个地方列了几个点。有没有同学分享一下拿 ES 存储的什么东西,跟大家分享一下你们的方案。

ES 的调优第二部分主要是从三个层面分享,存储成本、集群稳定性和集群性能。存储成本是字段存储选项和为字符串字段选择合适的格式。

首先看字段存储选项,ES 里面的数据有三份数据,倒排索引、行存和列存。行存是我们写入 ES 的数据,把整个结构体存下来,以便于你日后拉取数据。列存通常情况下用于聚合和排序,这个是把你写入的每个字段按列存储,这样方便快速拿到一列的数据。对于这三种存储我们其实并不是对于很多的使用场景中都用得到,比如说 ES 可以存储数据,我存很多数据的监控。最后想查某个 IP 的 CPU 曲线,我只需要拿到数据看曲线就可以了,这种场景可以把 cpu 字段的 index 关掉。比如说我的数据同样是统计分析的场景,我之后只是想拿这个数据看曲线,其是拿列存就可以了,原始数据没必要保存。打开最佳压缩后,经过我们发现最优的行存的成本能降 40%。

日志场景之类的,要保存日志的话,有一个 docvalues,我存的日志就想看文本,或者我存的评论,我对于字段聚合的要求不是特别高,我只是想放进去,我要的时候把整个结构体拿出来。

降低成本的第二方面:字符串。一个是 text,一个是 keywork。text 做分词,然后对每个词都可以建倒排索引,拿一个词可以搜到一整段话。如果仅仅是想把这个文本存进去并且取出来的话,后面都是可以去掉的。

第二种存储格式就是 keywork,你可以模糊匹配,但不能拿一个词搜索整段话,由于它不需要做分词,所以速度比较高。

集群的稳定性,要控制 index 的分片数,一个 index 是多个 shard 组成的,这个图中一个 index 有三个 shard,分布不同节点上,一个请求写几百个几千个文档在里面,当你的请求到达 ES 某个节点的时候会计算对于几千个文档里面分别属于哪个 shard,以 shard 为粒度分组。然后放到 BulkQueue 存储,BulkQueue 的默认值是 100,shard 数量过大的问题这个 Queue 就很容易被打满。但是 shard 数过小的话,就无法充分利用节点资源,我们给的建议并不适用于每个场景,具体看业务,100GB 以下的 index 设置 3-5 个 shard。大的 Index 单 shard 不超过 50GB。

提高集群性嫩控制 index 的副本数,每个 shard 可以指定多个副本,这个查询时,找到 shard 所在的节点,既可以主 shard 也可以从 shard。这是一种很常见的做法。

提高集群性能的持久化参数,就是我们之前讲了,默认情况下是 ES 每 1 秒产生一个新的 segment,如果我们对数据的可见性要求不是特别大的话,可以把这个放大一点。默认情况下 translog 是每写一次就刷盘一次,可以改成异步刷新。Merge 策略,底层的不断把小的合并成大的,但是默认情况下 ES 默认配置跟你这个节点 CPU 个数是相关的,CPU 个数越大,占的资源就越多,对于有几十个 CPU 的要控制一下,避免占用更多的资源。

还有提高性能的第三方面是写入方式。批量写入,我们通常推荐一般场景下在 1000-10000 之间都是可以的,可以根据业务的不同进行调整。写入的时候不指定 ID,你如果指定 ID,Lucene 首先会考虑你这个 ID 是否已经存在,如果有的话,就把原来的删掉,把现在的加进去,你每次都是写入和查询。若不指定 ID,ES 通过算法保证产生一个唯一的 ID,就免去了前面的查询,提高了集群效率。

另外就是 routing,避免小的查询过去轮询很多 shard,显著降低调度开销,提升查询效率。还有一个是让 index 压力分摊。最后一个是综合考虑 index 上的 shard 的数量,调度开销过大,可能会产生过程中有拒绝。



提高集群性能的查询方式,默认查询是 ES,会有打分逻辑,把你数据搜到以后,根据数据元信息计算分数出来,根据这个分数做一个排序,就是你这个数据和你的查询条件的相关性,类似于推荐的功能,如果说我们对于想要查询的数据,我们认为相关信息都是一样的,我只是精确的查到某条信息而已,通过下面的方式避免了打分的过程,提高了效率,Filter 的结果集是可以做缓存的,以后再使用就可以利用了。

最后一部分分享一下我们在 ES 内核的改进和我们增加的功能,主要是四个方面,一个是运管平台,每个系统都有。ES 优化方面有三个,降低索引成本,集群稳定性和集群性能。存储成本,一个是内存优化、冷热分离降低磁盘。提升集群稳定性。最后一个是提高集群的性能,做查询计划的优化,我们的优化不仅于此。

运管平台,各个业务的运管平台都差不多,首先有一个自动化的系统,提供集群的管理,官方的升级方案不允许新建 index,我们是支持的,你的集群在异常情况下,我们可以支持你升级。

插件管理,我们本身可以预装一些插件。Index 管理,数据过期,index Rollover 就是说我可以做一个逻辑表的概念,直接往这个逻辑表里面每天写就可以了,我会自动按照一定时间间隔创建 index,你不需要关心里面有多少 index。还有 Kibana、跨机房容灾、Dedicated Master,提升集群的稳定性。

ES 存储成本的倒排索引内存优化,倒排索引有一个词典和一个倒排表,ES 为了加速查询过程,把词典划分成一个一个的 block,前缀相同的词可以放到 block 里面,前缀相同的前缀树指向 block,在这个小范围内一个个匹配词典就可以了,这样可以快速加大性能,这个东西快是因为常驻内存。FST 虽然做了前缀和后缀的收敛,它是常驻在 JVM 里面的。我们为了利用压缩技术,通常 JVM 的配置在 32G 以下,对比较大的节点,Disk 比较大的情况下,普通场景 1T 数据需要 2GB 的内存,有很多人拿 JVM 索引,这个地方就更严重了。

我们目前初期方案调整了这个大小,内存使用率降低了 50%,我们现在正在考虑大容量的节点,降低使用成本,我们一个节点会大 20 个 T 这种,这种情况下,你的 JVM 就装不下了。LRU,我只存一部分,其他的放到磁盘,或者我们把这个东西拿到堆外,这是我们后面会做的事情,现在还在调研这块。

降低存储成本的另一个方法就是冷热分离,ES 本身可以对于每个 NODE 加一些属性在里面,你通过 ES 的一个命令可以强制把一个表所有数据搬迁到指定的一个属性的节点上面,所以我们在机器上架的时候可以同时加上两个属性,用户可以在前面 WEB 界面配置哪类的 index 多久之后迁移到冷机器,降低你的使用成本。我们后台就有一个调度器,会发送命令,从而降低你的使用成本。

稳定性优化,集群均衡策略,通常情况下我们随着业务的增长,比如一开始购买了三个节点,业务增长上来之后,你的磁盘使用率也好,慢慢增长,增长一定情况下,70-80% 的时候要做扩容,这里讲的是横向扩容,增加一个新的节点,之后 ES 本身的 Rebalance 逻辑会把大容量节点数据开始往小的节点上搬,这个没有问题,Rebalance 通过参数控制可以控制速度,问题在哪呢?问题是你此时新建 index,默认情况下,新增了 shard 绝大部分都会放在新的节点上,ES 认为这些磁盘使用率比较高,默认把大量的 Shard 放在小的节点上面,新增的 shard 读写压力比较大,这种情况下就造成单点过热的问题,这个默认就是跟 ES 算法有关,ES 算法里面会综合考虑当前节点上有多少 Shard 以及 index 的 shard 当前的集群分布,最后的结果,因为你新增的 index 在各个节点上没有 shard 存在,所以主要考虑了每个节点所有 shard 的分布,这样就会把新增的 shard 放在新增节点上面,虽然上面有两个数可以调的,但是这样依然不能解决问题,是因为底层的原数据的存储以及迭代规则有关系,就很容易造成有些节点分配的 shard 过多有些节点分配的 shard 过少。

这个主要解决的问题这个节点宕掉会产生很多 shard failed 的请求,这主要在大集群,比如说你有几十个节点,分配过会多出现这样的问题,我们把这个地方优化了一下,就是把重复的 SHARDFailed 请求的全部去掉,可以大大降低 MASTER 的压力。在大集群中发现新建一个 INDEX 很慢,这是可优化的地方。

集群稳定性优化的自研限流功能,ES 本身对于 JVM 判断不准,会导入写入量大的情况下会导致 OOM,我们在解析之前判定,做了一个曲线可以做到限流的功能。大的查询优化,查询的时候有可能查询到多个子查询,查询到第五次的时候会把你查询的所有结果做一个缓存。我们把这个地方也做了优化,根据你查询到数据量进行预估,超过一定量就不做缓存了。

我们的产品一个是 ES,一个是 CTSDB,我今天讲的是这篇文章提炼出来的点,这个会更系统,CTSDB 是我们的时序数据库,谢谢大家。

Q&A:

Q:我们公司在用 ES 的时候有一个场景发生了高负载,有 ES 节点挂,后面做恢复的时候启不起来,是内存的问题,内存只占 60% 多,但是就是启起不来。

A:我们往往碰到一个问题,一个节点挂掉之后难以加入到集群,有两个问题,一个在于 TCP 半连接队列的问题,一个是 ShardFailed 的问题。

Q:我主要想问一下 ES 有做日志分析,这个日志分析主要是做什么的?

A:ES 现在使用 50%-60% 场景是日志分析,可以扫描所有的本地日志文件,上传到 ES,有解析功能,把一段文字转化成一个结构,存储起来,然后你可以各种查询,然后可视化,做监控告警。

Q:我们只是做单纯的察看,你的日志分析是类似数据挖掘的吗?

A:我们这边因为还在跟官方谈,后面会接入这些能力。

Q:像这个产品,需求量特别大,今天有 500G,过期日志怎么处理?

A:通过冷热分离,单副本存储,磁盘是三副本。

Q:你们有做权限控制吗?

A:我们有自研的一套。

陈曦,腾讯高级工程师,腾讯 Elasticsearch Service 、CTSDB 后台开发者。多年云服务后台支撑系统研发经验,在日志分析、数据搜索、时序数据库研发、大型 Elasticsearch 集群调优等方面有较为丰富的实践经验。

PPT 下载: https://ask.qcloudimg.com/draft/1184429/nryi2kszw0.pdf


本文地址:http://www.6aiq.com/article/1571732516452
知乎专栏 点击关注
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出