京东到家的订单中心系统业务,无论是外部商户的订单生产,还是内部上下游系统的依赖,订单查询的调用量都非常大,导致订单数据读多写少.我们将订单数据存储在MySQL中,但是仅仅通过DB来支持大量的查询显然是不可取的。同时,对于一些复杂的查询,MySQL不够友好,所以订单中心系统使用Elasticsearch来承载订单查询的主要压力。Elasticsearch作为一个强大的分布式搜索引擎,支持近实时的数据存储和搜索,在京东到家的订单系统中发挥着巨大的作用。目前,订单中心ES集群存储10亿条文档,日均查询量达到5亿条。随着近几年京东到家业务的快速发展,订单中心的ES搭建方案也在不断演进。到目前为止,ES集群搭建是一套实时互备的方案,很好的保证了ES集群读写的稳定性。下面就给大家介绍一下这个过程以及过程中遇到的一些陷阱。ES集群架构演进之路1.初始阶段订单中心ES的初始阶段就像一张白纸。架设方案基本没有,很多配置保持集群默认配置。整个集群部署在集团的弹性云上,ES集群节点和机器部署混乱。同时,按照集群维度,一个ES集群会存在单点问题,这对于订单中心业务来说显然是不允许的。2、在集群隔离阶段,和很多业务一样,ES集群采用混合分布的方式。但是,由于订单中心ES存储的是在线订单数据,偶尔出现的混合分布式集群可能会占用大量系统资源,导致整个订单中心ES服务异常。显然,任何影响订单查询稳定性的情况都是不能容忍的。因此,针对这种情况,首先将弹性云中订单中心ES所在的集群节点搬出,ES集群情况略有好转。.但是随着集群数据的不断增加,弹性云的配置已经不能满足ES集群,为了完全的物理隔离,最终将订单中心ES集群部署在了高配置的物理机上,ES的性能集群得到改善。3.ES在节点复制调优阶段的性能与硬件资源有很大关系。ES集群单独部署在一台物理机上时,集群内部的节点不会独占整台物理机的资源。集群运行时,它们运行在同一台物理机上的节点仍然存在资源抢占的问题。因此,在这种情况下,为了让单个ES节点能够使用最大的机器资源,每个ES节点都部署在单独的物理机上。但是紧接着,问题又来了,单节点出现瓶颈怎么办?我们应该如何优化它?ES查询的原理是,当请求命中某个编号的分片时,如果不指定分片类型(Preference参数)查询,则请求会加载到对应分片号的各个节点。集群的默认副本配置是一主一副本。针对这种情况,我们想到了扩容副本的方法,从默认的一主一副本改为一主两副本,同时增加相应的物理机。订单中心ES集群搭建示意图如图所示。整个设置方式使用VIP对外部请求进行负载均衡:整个集群有一组primaryshard和两组secondaryshard(一主二从),从gateway节点转发过来的请求会在命中数据之前节点,它通过轮询来平衡。通过在集群中加入一组副本,扩展机器容量的方法,增加了集群的吞吐量,从而提高了整个集群的查询性能。下图是订单中心ES集群每个阶段的性能示意图,直观的展示了ES集群经过每个阶段的优化后性能的显着提升:当然还有分片数量和分片数量分片副本并不是越多越好。在这个阶段,我们选择合适的分片数量进行了进一步探索。分片数在MySQL中可以理解为分库分表,目前订单中心ES查询主要分为单ID查询和分页查询两种。分片数量越大,集群水平扩展规模越大,基于分片路由的单ID查询吞吐量可以大幅提升,但聚合分页查询性能会降低;分片数量越少,集群水平扩展规模越大。变小,单个ID的查询性能也会下降,但是分页查询的性能会提高。那么如何平衡分片数量和现有的查询业务,我们做了很多调整和压力测试,最终选择了集群性能更好的分片数量。4、至此主从集群调整阶段,订单中心的ES集群已经初具规模。但由于订单中心业务时效性要求高,因此对ES查询稳定性要求也高。如果集群中任何一个节点出现异常,查询服务都会受到影响,从而影响整个订单生产流程。显然,这种异常情况是致命的,所以为了应对这种情况,我们最初的想法是增加一个备集群。当主集群出现异常时,查询流量可以实时降级到备集群。备份集群应该如何设置?如何同步主备之间的数据?备集群应该存储什么样的数据?考虑到ES集群暂时没有很好的主备方案,为了更好的控制ES数据的写入,我们采用业务双写的方式搭建主备集群。每次业务操作需要写入ES数据时,同步写入主集群数据,然后异步写入备集群数据。同时,由于ES查询流量大部分来自最近几天的订单,而订单中心数据库数据有一套归档机制,将指定天数之前关闭的订单调入历史订单数据库。因此在归档机制中加入了删除备集群文档的逻辑,使得新建备集群中存储的订单数据与订单中心在线数据库中的数据量保持一致。同时在查询服务中使用ZK做流控切换,保证查询流量实时降级到备集群。至此,订单中心主从集群搭建完成,ES查询服务的稳定性大大提升。5、今日:双集群实时互备阶段,由于主集群ES版本低于1.7,目前ES稳定版已经迭代到6.x,新版ES不支持不仅大大优化了性能,还提供了一些新的好用的功能,所以我们在主集群上进行了版本升级,直接从原来的1.7升级到6.x版本。集群升级过程繁琐且冗长。不仅要保证对线上业务没有影响,而且升级是平滑的、无感知的。同时由于ES集群不支持从1.7到6.x跨多个版本的数据迁移,需要重建索引。升级主集群,具体升级过程这里不再赘述。主集群升级时,难免会出现不可用,但对于订单中心ES查询服务来说,这种情况是不允许的。因此在升级阶段,备集群临时充当主集群,支持所有在线ES查询,保证升级过程不影响正常在线业务。同时,对于线上业务,我们重新规划定义了两个集群,对承担的线上查询流量进行了重新划分。备集群存放的是最近几天在线的热点数据,数据量比主集群小很多,大约是主集群文档数量的十分之一。集群数据量小。在相同的集群部署规模下,备集群的性能优于主集群。但是在线上真实场景中,大部分线上查询流量也来自于热点数据,所以备集群就是用来承载这些热点数据的查询,备集群逐渐演变成热点数据集群。之前的主集群存储了全量数据,用这个集群来支撑剩下的一小部分查询流量。这部分查询主要是需要查询全量订单的特殊场景查询和订单中心系统内部查询。主集群也慢慢演变成冷数据集群。同时备份集群增加一键降级到主集群的功能。两个集群的状态同等重要,但都可以降级到另一个集群。双写策略也优化如下:假设有一个AB集群,普通同步方式写入master(A集群),异步方式写入backup(B集群)。当集群A发生异常时,同步写入集群B(主),异步写入集群A(备)。ES订单数据同步方案MySQL数据同步到ES,大致分为两种方案:方案一:监听MySQL的Binlog,分析Binlog,同步数据到ES集群。方案二:通过ESAPI直接向ES集群写入数据。考虑到订单系统ES服务的业务特殊性,订单数据的实时性要求比较高。显然,监听Binlog的方式相当于异步同步,可能会造成较大的延迟。而方案1本质上与方案2类似,只是引入了新的系统,同时也增加了维护成本。因此,订单中心ES采用直接通过ESAPI写入订单数据的方式。这种方式简单灵活,可以很好的满足订单中心数据同步到ES的需求。由于ES订单数据的同步写在业务中,当创建或更新文档出现异常时,重试必然会影响业务正常运行的响应时间。因此,对于每个业务操作,ES只更新一次。如果出现错误或异常,则向数据库中插入一个补救任务,一个Worker任务会实时扫描数据,并根据数据库订单数据再次更新ES数据。通过这种补偿机制,保证了ES数据和数据库订单数据的最终一致性。遇到的一些坑1.实时性要求高的查询走DB熟悉ES写机制的同学可能知道,新加入的文档会被收集到IndexingBuffer中,然后写入到文件系统缓存中。然后可以像任何其他文件一样对文件系统缓存进行索引。但是默认情况下,文档是每秒自动从IndexingBuffer刷新到文件系统缓存(也就是Refresh操作),所以这就是为什么我们说ES是近实时搜索而不是实时搜索时间:文档中的更改不会立即对搜索可见。但它会在一秒钟内变得可见。目前的订单系统ES采用默认的Refresh配置,所以对于那些订单数据实时性比较高的业务,直接使用数据库查询,保证数据的准确性。2.避免深度分页查询ES集群的分页查询支持from和size参数。查询时,每个分片都要构造一个长度为from+size的优先级队列,然后发回给网关节点,网关节点会对这些队列进行优先排序,找到正确大小的文档。假设一个索引有6个primaryshards,from为10000,size为10,每个shard要产生10010个结果,在gateway节点聚合60060个结果,最终找到10个符合要求的文档。可以看出,当from足够大时,即使不发生OOM,也会影响CPU和带宽,从而影响整个集群的性能。所以应该避免深度分页查询,尽量不要使用。3、FieldData和DocValuesFieldData在线查询偶尔会超时。通过调试查询语句,发现与排序有关。排序使用es1.x版本中的FieldData结构。FieldData占用JVMHeap内存,JVM内存有限。为FieldDataCache设置了一个阈值。如果空间不足,则使用最长未使用(LRU)算法移除FieldData,同时加载新的FieldDataCache。加载过程会消耗系统资源并花费大量时间。因此,这个查询的响应时间猛增,甚至影响了整个集群的性能。对于这种问题,解决方案是使用DocValues。DocValuesDocValues是一个列式数据存储结构,和FieldData很相似,但是它的存储位置是在Lucene文件中,即不会占用JVMHeap。随着ES版本的迭代,DocValues比FieldData更稳定,DocValues从2.x开始就是默认设置。总结架构的快速迭代源于业务的快速发展。正是由于近年来到家业务的快速发展,订单中心的结构也在不断优化升级。没有最好的架构方案,只有最合适的方案。相信再过几年,订单中心的架构会焕然一新,但吞吐量更大、性能更好、稳定性更强的就是订单中心系统。永恒的追求。
