
00node
导读web
滴滴 ElasticSearch 平台承接了公司内部全部使用 ElasticSearch 的业务,包括核心搜索、RDS从库、日志检索、安全数据分析、指标数据分析等等。平台规模达到了3000+节点,5PB 的数据存储,超过万亿条数据。平台写入的峰值写入 TPS 达到了2000w/s,天天近 10 亿次检索查询。为了承接这么大的体量和丰富的使用场景,滴滴 ElasticSearch 须要解决稳定性、易用性、性能、成本等诸多问题。安全
咱们在4年多的时间里,作了大量优化,积攒了很是丰富的经验。经过建设滴滴搜索平台,打造滴滴ES引擎,全方位提高用户使用ElasticSearch体验。此次给你们分享的是滴滴在写入性能优化的实践,优化后,咱们将ES索引的写入性能翻倍,结合数据冷热分离场景,支持大规格存储的物理机,给公司每一年节省千万左右的服务器成本。性能优化
01服务器
背景微信
前段时间,为了下降用户使用ElasticSearch的存储成本,咱们作了数据的冷热分离。为了保持集群磁盘利用率不变,咱们减小了热节点数量。ElasticSearch集群开始出现写入瓶颈,节点产生大量的写入rejected,大量从kafka同步的数据出现写入延迟。咱们深刻分析写入瓶颈,找到了突破点,最终将Elasticsearch的写入性能提高一倍以上,解决了ElasticSearch瓶颈致使的写入延迟。markdown
这篇文章介绍了咱们是如何发现写入瓶颈,并对瓶颈进行深刻分析,最终进行了创新性优化,极大的提高了写入性能。网络
02dom
写入瓶颈分析异步
2.1 发现瓶颈
咱们去分析这些延迟问题的时候,发现了一些不太好解释的现象。以前作性能测试时,ES节点cpu利用率能超过80%,而生产环境延迟索引所在的节点cpu资源只使用了不到50%,集群平均cpu利用率不到40%,这时候IO和网络带宽也没有压力。经过提高写入资源,写入速度基本没增长。因而咱们开始一探究竟,咱们选取了一个索引进行验证,该索引使用10个ES节点。从下图看到,写入速度不到20w/s,10个ES节点的cpu,峰值在40-50%之间。

为了确认客户端资源是足够的,在客户端不作任何调整的状况下,将索引从10个节点,扩容到16个节点,从下图看到,写入速度来到了30w/s左右。

这证实了瓶颈出在服务端,ES节点扩容后,性能提高,说明10个节点写入已经达到瓶颈。可是上图能够看到,CPU最多只到了50%,并且此时IO也没达到瓶颈。
2.2 ES写入模型说明
这里要先对ES写入模型进行说明,下面分析缘由会跟写入模型有关。

客户端通常是准备好一批数据写入ES,这样能极大减小写入请求的网络交互,使用的是ES的BULK接口,请求名为BulkRequest。这样一批数据写入ES的ClientNode。ClientNode对这一批数据按数据中的routing值进行分发,组装成一批BulkShardRequest请求,发送给每一个shard所在的DataNode。发送BulkShardRequest请求是异步的,可是BulkRequest请求须要等待所有BulkShardRequest响应后,再返回客户端。
2.3 寻找缘由
咱们在ES ClientNode上有记录BulkRequest写入slowlog。
`items` 是一个BulkRequest的发送请求数
`totalMills` 是BulkRequest请求的耗时
`max` 记录的是耗时最长的BulkShardRequest请求
`avg` 记录的是全部BulkShardRequest请求的平均耗时。
我这里截取了部分示例。
[xxx][INFO ][o.e.m.r.RequestTracker ] [log6-clientnode-sf-5aaae-10] bulkDetail||requestId=null||size=10486923||items=7014||totalMills=2206||max=2203||avg=37[xxx][INFO ][o.e.m.r.RequestTracker ] [log6-clientnode-sf-5aaae-10] bulkDetail||requestId=null||size=210506||items=137||totalMills=2655||max=2655||avg=218
从示例中能够看到,2条记录的avg相比max都小了不少。一个BulkRequest请求的耗时,取决于最后一个BulkShardRequest请求的返回。这就很容易联想到分布式系统的长尾效应。

接下来再看一个现象,咱们分析了某个节点的write线程的状态,发现节点有时候write线程全是runnable状态,有时候又有大量在waiting。此时写入是有瓶颈的,runnable状态能够理解,但却常常出现waiting状态。因此这也能印证了CPU利用率不高。同时也论证长尾效应的存在,由于长尾节点繁忙,ClientNode在等待繁忙节点返回BulkShardRequest请求,其余节点可能出现相对空闲的状态。下面是一个节点2个时刻的线程状态:
时刻一:

时刻二:

2.4 瓶颈分析
谷歌大神Jeffrey Dean《The Tail At Scale》介绍了长尾效应,以及致使长尾效应的缘由。总结下来,就是正常请求都很快,可是偶尔单次请求会特别慢。这样在分布式操做时会致使长尾效应。咱们从ES原理和实现中分析,形成ES单次请求特别慢的缘由。发现了下面几个因素会形成长尾问题:
# 2.4.1 lucene refresh
咱们打开lucene引擎内部的一些日志,能够看到:

write线程是用来处理BulkShardRequest请求的,可是从截图的日志能够看到,write线程也会会进行refresh操做。这里面的实现比较复杂,简单说,就是ES按期会将写入buffer的数据refresh成segment,ES为了防止refresh不过来,会在BulkShardRequest请求的时候,判断当前shard是否有正在refresh的任务,有的话,就会帮忙一块儿分摊refresh压力,这个是在write线程中进行的。这样的问题就是形成单次BulkShardRequest请求写入很慢。还致使长时间占用了write线程。在write queue的缘由会具体介绍这种危害。

# 2.4.2 translog ReadWriteLock
ES的translog相似LSM-Tree的WAL log。ES实时写入的数据都在lucene内存buffer中,因此须要依赖写入translog保证数据的可靠性。ES translog具体实现中,在写translog的时候会上ReadLock。在translog过时、翻滚的时候会上WriteLock。这会出现,在WriteLock期间,实时写入会等待ReadLock,形成了BulkShardRequest请求写入变慢。咱们配置的tranlog写入模式是async,正常开销是很是小的,可是从图中能够看到,写translog偶尔可能超过100ms。

# 2.4.3 write queue

ES DataNode的写入是用标准的线程池模型是,提供一批active线程,咱们通常配置为跟cpu个数相同。而后会有一个write queue,咱们配置为1000。DataNode接收BulkShardRequest请求,先将请求放入write queue,而后active线程有空隙的,就会从queue中获取BulkShardRequest请求。这种模型下,当写入active线程繁忙的时候,queue中会堆积大量的请求。这些请求在等待执行,而从ClientNode角度看,就是BulkShardRequest请求的耗时变长了。下面日志记录了action的slowlog,其中waitTime就是请求等待执行的时间,能够看到等待时间超过了200ms。
[xxx][INFO ][o.e.m.r.RequestTracker ] [log6-datanode-sf-4f136-100] actionStats||action=indices:data/write/bulk[s][p]||requestId=546174589||taskId=6798617657||waitTime=231||totalTime=538[xxx][INFO ][o.e.m.r.RequestTracker ] [log6-datanode-sf-4f136-100] actionStats||action=indices:data/write/bulk[s][p]||requestId=546174667||taskId=6949350415||waitTime=231||totalTime=548
# 2.4.4 JVM GC
ES正常一次写入请求基本在亚毫秒级别,可是jvm的gc可能在几十到上百毫秒,这也增长了BulkShardRequest请求的耗时。这些加剧长尾现象的case,会致使一个状况就是,有的节点很繁忙,发往这个节点的请求都delay了,而其余节点却空闲下来,这样总体cpu就没法充分利用起来。
2.5 论证结论
长尾问题主要来自于BulkRequest的一批请求会分散写入多个shard,其中有的shard的请求会由于上述的一些缘由致使响应变慢,形成了长尾。若是每次BulkRequest只写入一个shard,那么就不存在写入等待的状况,这个shard返回后,ClientNode就能将结果返回给客户端,那么就不存在长尾问题了。
咱们作了一个验证,修改客户端SDK,在每批BulkRequest写入的时候,都传入相同的routing值,而后写入相同的索引,这样就保证了BulkRequest的一批数据,都写入一个shard中。

优化后,第一个平稳曲线是,每一个bulkRequest为10M的状况,写入速度在56w/s左右。以后将bulkRequest改成1M(10M差很少有4000条记录,以前写150个shard,因此bulkSize比较大)后,性能还有进一步提高,达到了65w/s。
从验证结果能够看到,每一个bulkRequest只写一个shard的话,性能有很大的提高,同时cpu也能充分利用起来,这符合以前单节点压测的cpu利用率预期。
03
性能优化
从上面的写入瓶颈分析,咱们发现了ES没法将资源用满的缘由来自于分布式的长尾问题。因而咱们着重思考如何消除分布式的长尾问题。而后也在探寻其余的优化点。总体性能优化,咱们分红了三个方向:
横向优化,优化写入模型,消除分布式长尾效应;
纵向优化,提高单节点写入能力;
应用优化,探究业务节省资源的可能。
此次的性能优化,咱们在这三个方向上都取得了一些突破。
3.1 优化写入模型
写入模型的优化思路是将一个BulkRequest请求,转发到尽可能少的shard,甚至只转发到一个shard,来减小甚至消除分布式长尾效应。咱们完成的写入模型优化,最终能作到一个BulkRequest请求只转发到一个shard,这样就消除了分布式长尾效应。
写入模型的优化分红两个场景。一个是数据不带routing的场景,这种场景用户不依赖数据分布,比较容易优化的,能够作到只转发到一个shard。另外一个是数据带了routing的场景,用户对数据分布有依赖,针对这种场景,咱们也实现了一种优化方案。
# 3.1.1 不带routing场景
因为用户对routing分布没有依赖,ClientNode在处理BulkRequest请求中,给BulkRequest的一批请求带上了相同的随机routing值,而咱们生成环境的场景中,一批数据是写入一个索引中,因此这一批数据就会写入一个物理shard中。

# 3.1.2 带routing场景
下面着重介绍下咱们在带routing场景下的实现方案。这个方案,咱们须要在ES Server层和ES SDK都进行优化,而后将二者综合使用,来达到一个BulkRequest上的一批数据写入一个物理shard的效果。优化思路ES SDK作一次数据分发,在ES Server层作一次随机写入来让一批数据写入同一个shard。
先介绍下Server层引入的概念,咱们在ES shard之上,引入了逻辑shard的概念,命名为`number_of_routing_size` 。ES索引的真实shard咱们称之为物理shard,命名是`number_of_shards`。
物理shard必须是逻辑shard的整数倍,这样一个逻辑shard能够映射到多个物理shard。一组逻辑shard,咱们命名为slot,slot总数为`number_of_shards / number_of_routing_size`。
数据在写入ClientNode的时候,ClientNode会给BulkRequest的一批请求生成一个相同的随机值,目的是为了让写入的一批数据,都能写入相同的slot中。数据流转如图所示:

最终计算一条数据所在shard的公式以下:
slot = hash(random(value)) % (number_of_shards/number_of_routing_size)shard_num = hash(_routing) % number_of_routing_size + number_of_routing_size * slot
而后咱们在ES SDK层进一步优化,在BulkProcessor写入的时候增长逻辑shard参数,在add数据的时候,能够按逻辑shard进行hash,生成多个BulkRequest。这样发送到Server的一个BulkRequest请求,只有一个逻辑shard的数据。最终,写入模型变为以下图所示:

通过SDK和Server的两层做用,一个BulkRequest中的一批请求,写入了相同的物理shard。
这个方案对写入是很是友好的,可是对查询会有些影响。因为routing值是对应的是逻辑shard,一个逻辑shard要对应多个物理shard,因此用户带routing的查询时,会去一个逻辑shard对应的多个物理shard中查询。
咱们针对优化的是日志写入的场景,日志写入场景的特征是写多读少,并且读写比例差异很大,因此在实际生产环境中,查询的影响不是很大。
3.2 单节点写入能力提高
单节点写入性能提高主要有如下优化:
backport社区优化,包括下面2方面:
merge 社区flush优化特性:[#27000] Don't refresh on `_flush` `_force_merge` and `_upgrade`
merge 社区translog优化特性,包括下面2个:
[#45765] Do sync before closeIntoReader when rolling generation to improve index performance
[#47790] sync before trimUnreferencedReaders to improve index preformance
这些特性咱们在生产环境验证下来,性能大概能够带来18%的性能提高。
咱们还作了2个可选性能优化点:
优化translog,支持动态开启索引不写translog,不写translog的话,咱们能够再也不触发translog的锁问题,也能够缓解了IO压力。可是这可能带来数据丢失,因此目前咱们作成动态开关,能够在须要追数据的时候临时开启。后续咱们也在考虑跟flink团队结合,经过flink checkpoint保证数据可靠性,就能够不依赖写入translog。从生产环境咱们验证的状况看,在写入压力较大的索引上开启不写translog,能有10-30%不等的性能提高。
优化lucene写入流程,支持在索引上配置在write线程不一样步flush segment,解决前面提到长尾缘由中的lucene refresh问题。在生产环境上,咱们验证下来,能有7-10%左右的性能提高。
# 3.2.1 业务优化
在本次进行写入性能优化探究过程当中,咱们还和业务一块儿发现了一个优化点,业务的日志数据中存在2个很大的冗余字段(args、response),这两个字段在日志原文中存在,还另外用了2个字段存储,这两个字段并无加索引,日志数据写入ES时能够不从日志中解析出这2个字段,在查询的时候直接从日志原文中解析出来。
不清洗大的冗余字段,咱们验证下来,能有20%左右的性能提高,该优化同时还带来了10%左右存储空间节约。
04
生产环境性能提高结果
4.1 写入模型优化
咱们重点看下写入模型优化的效果,下面的优化,都是在客户端、服务端资源没作任何调整的状况下的生产数据。
下图所示索引开启写入模型优化后,写入tps直接从50w/s,提高到120w/s。

生产环境索引写入性能的提高比例跟索引混部状况、索引所在资源大小(长尾问题影响程度)等因素影响。从实际优化效果看,不少索引都能将写入速度翻倍,以下图所示:

4.2 写入拒绝量(write rejected)降低
而后再来看一个关键指标,写入拒绝量(write rejected)。ES datanode queue满了以后就会出现rejected。
rejected异常带来个危害,一个是个别节点出现rejected,说明写入队列满了,大量请求在队列中等待,而region内的其余节点却可能很空闲,这就形成了cpu总体利用率上不去。
rejected异常另外一个危害是形成失败重试,这加剧了写入负担,增长了写入延迟的可能。
优化后,因为一个bulk请求再也不分到每一个shard上,而是写入一个shard。一来减小了写入请求,二来再也不须要等待所有shard返回。

4.3 延迟状况缓解
最后再来看下写入延迟问题。通过优化后,写入能力获得大幅提高后,极大的缓解了当前的延迟状况。下面截取了集群优化先后的延迟状况对比。

05
总结
此次写入性能优化,滴滴ES团队取得了突破性进展。写入性能提高后,咱们用更少的SSD机器支撑了数据写入,支撑了数据冷热分离和大规格存储物理机的落地,在这过程当中,咱们下线了超过400台物理机,节省了每一年千万左右的服务器成本。在整个优化过程当中,咱们深刻分析ES写入各个环节的耗时状况,去探寻每一个耗时环节的优化点,对ES写入细节有了更加深入的认识。咱们还在持续探寻更多的优化方式。并且咱们的优化不只在写入性能上。在查询的性能和稳定性,集群的元数据变动性能等等方面也都在不断探索。咱们也在持续探究如何给用户提交高可靠、高性能、低成本、更易用的ES,将来会有更多干货分享给你们。
正文完
做者:魏子珺
滴滴专家工程师
本文编辑:筷子
活动预告

腾讯云Elasticsearch在腾讯云客户及腾讯各个内部业务中,普遍应用于日志实时分析、结构化数据分析、全文检索等场景。腾讯云联合Elastic推出了涵盖了所有商业化功能的内核加强版。在内外部客户海量数据规模,众多的应用场景下,对原生ES进行了高扩展性,高可用,性能,成本等全方位的优化。
本次分享主要剖析腾讯对 Elasticsearch 海量规模下的优化与实践,但愿能和广大 ES 爱好者共同探讨推进 ES 技术的发展。
参与方式:活动直播将采用 Zoom 会议方式进行
时间:2020年8月15日 15:00 - 16:00(北京时间)
会议 ID:590-508-385
会议密码:elastic
请于活动开始前 5 分钟拨入并等待活动开始。
嗨,互动起来吧!
喜欢这篇文章么?
欢迎留下你想说的,留言 100% 精选哦!
Elastic 社区公众号长期征稿,若是您有 Elastic 技术的相关文章,也欢迎投稿至本公众号,一块儿进步! 投稿请添加微信:medcl123
招聘信息
Job board
社区招聘栏目是一个新的尝试,帮助社区的小伙伴找到心仪的职位,也帮助企业找到所需的人才,为伯乐和千里马牵线搭桥。有招聘需求的企业和正在求职的社区小伙伴,能够联系微信 medcl123 提交招聘需求和发布我的简历信息。
关
注
我
们
Elastic中文社区公众号 (elastic-cn)
为您聚集 Elastic 社区的最新动态、精选干货文章、精华讨论、文档资料、翻译与版本发布等。

喜欢本篇内容就请给咱们点个[在看]吧

本文分享自微信公众号 - Elastic中文社区(elastic-cn)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。