关于数据库:滴滴ElasticSearch千万级TPS写入性能翻倍技术剖析



桔妹导读:滴滴ElasticSearch平台承接了公司外部所有应用ElasticSearch的业务,包含外围搜寻、RDS从库、日志检索、平安数据分析、指标数据分析等等。平台规模达到了3000+节点,5PB 的数据存储,超过万亿条数据。平台写入的峰值写入TPS达到了2000w/s,每天近 10 亿次检索查问。为了承接这么大的体量和丰盛的应用场景,滴滴ElasticSearch须要解决稳定性、易用性、性能、老本等诸多问题。咱们在4年多的工夫里,做了大量优化,积攒了十分丰盛的教训。通过建设滴滴搜寻平台,打造滴滴ES引擎,全方位晋升用户应用ElasticSearch体验。这次给大家分享的是滴滴在写入性能优化的实际,优化后,咱们将ES索引的写入性能翻倍,联合数据冷热拆散场景,反对大规格存储的物理机,给公司每年节俭千万左右的服务器老本。

1.背景

前段时间,为了升高用户应用ElasticSearch的存储老本,咱们做了数据的冷热拆散。为了放弃集群磁盘利用率不变,咱们缩小了热节点数量。ElasticSearch集群开始呈现写入瓶颈,节点产生大量的写入rejected,大量从kafka同步的数据呈现写入提早。咱们深入分析写入瓶颈,找到了突破点,最终将Elasticsearch的写入性能晋升一倍以上,解决了ElasticSearch瓶颈导致的写入提早。这篇文章介绍了咱们是如何发现写入瓶颈,并对瓶颈进行深入分析,最终进行了创新性优化,极大的晋升了写入性能。

2.写入瓶颈剖析

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利用率预期。

3. 性能优化

从下面的写入瓶颈剖析,咱们发现了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%左右存储空间节约。

4. 生产环境性能晋升后果

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 提早状况缓解

最初再来看下写入提早问题。通过优化后,写入能力失去大幅晋升后,极大的缓解了以后的提早状况。上面截取了集群优化前后的提早状况比照。

5.总结

这次写入性能优化,滴滴ES团队获得了突破性停顿。写入性能晋升后,咱们用更少的SSD机器撑持了数据写入,撑持了数据冷热拆散和大规格存储物理机的落地,在这过程中,咱们下线了超过400台物理机,节俭了每年千万左右的服务器老本。在整个优化过程中,咱们深入分析ES写入各个环节的耗时状况,去探寻每个耗时环节的优化点,对ES写入细节有了更加粗浅的意识。咱们还在继续探寻更多的优化形式。而且咱们的优化不仅在写入性能上。在查问的性能和稳定性,集群的元数据变更性能等等方面也都在一直摸索。咱们也在继续探索如何给用户提交高牢靠、高性能、低成本、更易用的ES,将来会有更多干货分享给大家。

团队介绍

滴滴云平台事业群滴滴搜寻平台在开源 Elasticsearch 根底上提供企业级的海量数据的 binlog 数仓,数据分析、日志搜寻,全文检索等场景的服务。 通过多年的技术积淀,基于滴滴深度定制的Elasticsearch内核,打造了稳固易用,低成本、高性能的搜寻服务。滴滴搜寻平台除了服务滴滴外部应用Elasticsearch的全副业务,还在进行商业化输入,已和多家公司开展商业单干。目前团队外部有三位Elasticsearch Contributor。

作者简介

滴滴Elasticsearch引擎负责人,负责率领引擎团队深刻Elasticsearch内核,解决在海量规模下Elasticsearch遇到的稳定性、性能、老本方面的问题。曾在隆重、网易工作,有丰盛的引擎建设教训。

延长浏览

内容编辑 | Charlotte
分割咱们 | DiDiTech@didiglobal.com

滴滴技术 出品

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理