随着订单量的增长、业务复杂度的提升,订单系统也在不断演变进化,从早期一个订单业务模块到现在分布式可扩展的高并发、高性能、高可用订单系统。整个发展过程中,订单系统经历了几个明显的阶段,通过不同的技术优化方案解决业务上遇到的问题。
1、京东到家系统架构
2、订单数据入库流程
用户提单以后数据怎么流转?提单其实是一个把用户下单数据存储到数据库,提单系统做了一些分库分表。那么提完单的数据怎么下发到订单系统生产?
首先我们会有一个管道,提单通过一个分布式异步任务来下发订单管道里。所有的订单下来都会放到管道里,我们通过一个异步的任务,按照一定的速率,均匀地把订单下发到订单生产系统。
这样设计有一个好处,比如像大促时可能会有大量数据一下子下发到订单生产系统,对订单生产库有很大压力,所以我们中间设计出一个管道,通过异步任务来分发生产订单。
其实个人订单DB跟订单系统是不同维度的数据,因为个人订单其实是基于用户去做了一个分库分表,它每一个查询的订单都是基于这种个人,跟订单生产是不一样的,所以每个维度的数据都是单独的存储,来提高系统的稳定性,以及适合它自身业务特性的设计。
那么订单系统跟个人中心是怎么交互的?首先异步,我们是通过MQ来交互这些订单状态的变更。另外C端的订单取消,是怎么同步到订单生产系统的?我们是通过RPC的调用来保证订单实时取消,有一个返回结果。
3、订单系统微服务及架构演进
订单系统的架构演进如下图所示
2017-2018年,我们根据2016年遇到的问题做了一些拆分,比如按领域拆分不同的APP应用。这样拆分做到的就是系统没有单点,负载均衡可以横向扩展,多点部署。包括引入Redis,其实我们用到了Redis的分布式锁、缓存、有序队列、定时任务。
我们数据库为什么升级?因为数据库的数据量越来越大,比如添加一些字段,它其实会做一些锁表操作,随着数据量越大,单表的数据越来越多,数据主从延迟以及一些锁表的时间会越来越长
所以在加字段的时候对生产影响特别大,我们就会对数据做一个分离,把一些冷的数据单独做一个历史库,剩下的生产库只留最近几天的一些生产需要的数据,这样生产库的订单数据量就会很小,每次修改表的时间是可控的,所以我们会把数据按照冷备进行拆分。
至于为什么引入ES,是因为订单在生产方面会有一些很复杂的查询,复杂查询对数据库的性能影响非常大,引入ES就可以很好地解决这个问题。
2018-2019年,我们发现之前在引入数据库时,用数据冗余来保证一些数据应用可互备互降。比如我们之前在用ES低版本1.7的时候,其实就是一个单点,当集群有问题时是会影响生产的。
我们后来引入了一个双集群,双ES集群互备,当一个集群有问题时,另一个集群可以直接顶上来,确保了应用的高可用和生产没有问题。
数据的演进最终结构如上图,当这是基于目前业务的一个支撑,在未来业务不断发展的情况下,这个数据库架构是远远不够的。
基于以上架构,我们主要是做到了一主多从的主备实时切换,同时确保主从在不同机房来保证数据库的容灾能力。同时通过业务隔离查询不同的从库给主库减轻压力,以及冷备数据的隔离的一个思路来保证订单数据库的稳定性。
4、ElasticSearch集群
最开始我们是单ES集群,DB会通过一个同步写写到ES集群。这个时候我们的ES是一个单机群,如果写失败的话,我们会起一个异步任务来保证数据的最终一致性,是这样的一个架构。
在ES集群没问题的情况下,这个架构也是没问题的,但当集群有问题时,其实就没有可降级的了
为了解决这个问题,我们引入了ES的冷备两个集群,热集群只保存跟数据库一样的生产库的数据,比如说我们现在保证的就是5天的生产数据,其它所有数据都归档在一个ES的冷集群里。
通过这种异步跟同步写,通过异步任务来保证最终的集群的数据一致性。这就是ES的架构升级
5、订单系统结构
如上图所示,是我们整个订单系统的结构。整个过程我们是通过业务网关、RPC高可用、业务聚合、DB冗余、多机房部署,来保证整个订单应用的一些系统架构高可用。上述就是整体的订单架构演进过程。
6、订单系统容灾能力
就像上面提到的,我们对开放平台、商家中心、京明管家等业务系统的支撑怎么做到互备?
其实就是通过ES的冷热集群,冷集群存全量的数据,热集群存最近几天的生产数据。
而Redis是做业务隔离,Redis 存储有一些大key会影响核心业务,我们就会把非核心的业务拆出来,拆到另外一个Redis集群。
这就是我们系统的业务隔离和集群的互备。
7、QA
Q1:集群规模大概是什么样的?各集群节点规模如何?
A:京东到家订单中心ES 集群目前大约有将近30亿文档数,数据大小约1.3TB,集群结构是8个主分片,每个主分片有两个副本,共24个分片。
每个机器上分布1-2个分片,如果企业不差钱最好的状态就是每个分片独占一台机器。这些集群规模和架构设计不应该是固定的,每一个业务系统应该根据自身实际业务去规划设计
这样做确定分片数:
Q4:ES主要是用于明细单查询,还是聚合统计?Join对资源耗用大吗?如何控制内存及优化?
A:ES在订单系统中的实践主要是解决复杂查询的问题,ES不建议使用聚合统计,如果非要使用那我也拦不住你,哈哈哈。
8、参考:京东把 Elasticsearch 用得真牛逼!日均5亿订单查询完美解决!
京东到家订单中心系统业务中,无论是外部商家的订单生产,或是内部上下游系统的依赖,订单查询的调用量都非常大,造成了订单数据读多写少的情况。
我们把订单数据存储在MySQL中,但显然只通过DB来支撑大量的查询是不可取的。同时对于一些复杂的查询,MySQL支持得不够友好,所以订单中心系统使用了Elasticsearch来承载订单查询的主要压力
ES查询的原理,当请求打到某号分片的时候,如果没有指定分片类型(Preference参数)查询,请求会负载到对应分片号的各个节点上。
而集群默认副本配置是一主一副,针对此情况,我们想到了扩容副本的方式,由默认的一主一副变为一主二副,同时增加相应物理机
下图为订单中心ES集群各阶段性能示意图,直观地展示了各阶段优化后ES集群性能的显著提升:
当然分片数量和分片副本数量并不是越多越好。分片数可以理解为MySQL中的分库分表,而当前订单中心ES查询主要分为两类:单ID查询以及分页查询。
分片数越大,集群横向扩容规模也更大,根据分片路由的单ID查询吞吐量也能大大提升,但聚合的分页查询性能则将降低;
分片数越小,集群横向扩容规模也更小,单ID的查询性能也会下降,但分页查询的性能将会提升。
以如何均衡分片数量和现有查询业务,我们做了很多次调整压测,最终选择了集群性能较好的分片数。
发文时业务体量:美团外卖由日均几单发展为日均500万单(9月11日已突破600万)
1、订单架构
随着订单量的增长、业务复杂度的提升,外卖订单系统也在不断演变进化,从早期一个订单业务模块到现在分布式可扩展的高性能、高可用、高稳定订单系统。整个发展过程中,订单系统经历了几个明显的阶段,重点关注各阶段的业务特征、挑战及应对之道。
外卖订单业务是一个需要即时送的业务,对实时性要求很高。从用户订餐到最终送达用户,一般在1小时内。如果最终送达用户时间变长,会带来槽糕的用户体验。
在1小时内,订单会快速经过多个阶段,直到最终送达用户。各个阶段需要紧密配合,确保订单顺利完成。下图是一个用户视角的订单流程图。
从普通用户的角度来看,一个外卖订单从下单后,会经历支付、商家接单、配送、用户收货、售后及订单完成多个阶段。
以技术的视角来分解的话,每个阶段依赖于多个子服务来共同完成,比如下单会依赖于购物车、订单预览、确认订单服务,这些子服务又会依赖于底层基础系统来完成其功能。
**第一阶段:**早期,外卖整体架构简单、灵活,公共业务逻辑通过jar包实现后集成到各端应用,应用开发部署相对简单。比较适合业务早期逻辑简单、业务量较小、需要快速迭代的情况。
随着业务的发展以及业务的逐步成熟,业务大框架基本成型,业务在大框架基础上快速迭代。大家共用一个大项目进行开发部署,相互影响,协调成本变高;多个业务部署于同一VM,相互影响的情况也在增多。
为解决开发、部署、运行时相互影响的问题。我们将订单系统进行独立拆分,从而独立开发、部署、运行,避免受其它业务影响。系统拆分主要有如下几个原则:
基于以上原则,我们将订单系统进行独立拆分,所有订单服务通过RPC接口提供给外部使用。订单系统内部,我们将功能按优先级拆分为不同子系统,避免相互影响。订单系统通过MQ(队列)消息,通知外部订单状态变更。
**第二阶段:**独立拆分后的订单系统架构如下所示:
其中,最底层是数据存储层,订单相关数据独立存储。订单服务层,我们按照优先级将订单服务划分为三个系统,分别为交易系统、查询系统、异步处理系统。
订单系统经过上述独立拆分后,有效地避免了业务间的相互干扰,保障迭代速度的同时,保证了系统稳定性。
这时,我们的订单量突破百万,而且还在持续增长。之前的一些小问题,在订单量增加后,被放大,进而影响用户体验。
比如,用户支付成功后,极端情况下(比如网络、数据库问题)会导致支付成功消息处理失败,用户支付成功后依然显示未支付。
订单量变大后,问题订单相应增多。我们需要提高系统的可靠性,保证订单功能稳定可用。
为了提供更加稳定、可靠的订单服务,我们对拆分后的订单系统进行进一步升级。下面将分别介绍升级涉及的主要内容
1)、性能优化
**2)、一致性优化:**订单系统是一个复杂的分布式系统,比如支付涉及订单系统、支付平台、支付宝/网银等第三方。仅通过传统的数据库事务来保障不太可行。对于订单交易系统的事务性,并不要求严格满足传统数据库事务的ACID性质,只需要最终结果一致即可。
3)、高可用: 针对订单系统而言,其主要组成组件包括三类:存储层、中间件层、服务层
**第三阶段:**存储层的可扩展性改造,主要是对MySQL扩展性改造
针对订单表超过单库容量的问题,需要进行分表操作,即将订单表数据进行拆分。单表数据拆分后,解决了写的问题,但是如果查询数据不在同一个分片,会带来查询效率的问题(需要聚合多张表)。
具体来说,外卖主要涉及三个查询维度:订单ID、用户ID、门店ID。对订单表分表时,对于一个订单,我们存三份,分别按照订单ID、用户ID、 门店ID以一定规则存储在每个维度不同分片中。
这样,可以分散写压力,同时,按照订单ID、用户ID、门店ID三个维度查询时,数据均在一个分片,保证较高的查询效率。
订单表分表后,订单表的存储架构如下所示:
可以看到,分表后,每个维度共有100张表,分别放在4个库上面。对于同一个订单,冗余存储了三份。未来,随着业务发展,还可以继续通过将表分到不同机器上来持续获得容量的提升。
分库分表后,订单数据存储到多个库多个表中,为应用层查询带来一定麻烦,解决分库分表后的查询主要有三种方案:MySQL服务器端、中间件、应用层
通过分库分表,解决了写容量扩展问题。但是分表后,会给查询带来一定的限制,只能支持主要维度的查询,其它维度的查询效率存在问题。
2、ES搜索
订单表分表之后,对于ID、用户ID、门店ID外的查询(比如按照手机号前缀查询)存在效率问题。这部分通常是复杂查询,可以通过全文搜索来支持。
在订单系统中,我们通过ES来解决分表后非分表维度的复杂查询效率问题。具体来说,使用ES,主要涉及如下几点
vivo Elasticsearch集群应用实践参见:https://elasticsearch.cn/slides/287#page=5
集群业务规模及集群配置:
1、订单系统架构
随着用户量级的快速增长,将订单模块从商城拆分出来,独立为订单系统,使用独立的数据库,为商城相关系统提供订单、支付、物流、售后等标准化服务。
2、技术挑战
1)、数据量和高并发问题
我们知道InnoDB存储引擎的存储结构是B+树,查找时间复杂度是O(log n),因此当数据总量n变大时,检索速度必然会变慢, 不论如何加索引或者优化都无法解决,只能想办法减小单表数据量。
数据量大的解决方案有:数据归档、分表
单机MySQL的处理能力是有限的,当压力过大时,所有请求的访问速度都会下降,甚至有可能使数据库宕机。
并发量高的解决方案有:使用缓存、读写分离、分库
方案进行简单描述:
订单数据的更新操作较多,下单高峰时主库的压力依然没有得到解决。且存在主从同步延迟,正常情况下延迟非常小,不超过1ms,但也会导致在某一个时刻的主从数据不一致。那就需要对所有受影响的业务场景进行兼容处理,可能会做一些妥协;
3、分库分表技术选型
参考之前项目经验,并与公司中间件团队沟通后,采用了开源的 Sharding-JDBC 方案。现已更名为Sharding-Sphere
4、分库分表策略
结合业务特性,选取用户标识作为分片键,通过计算用户标识的哈希值再取模来得到用户订单数据的库表编号.
假设共有n个库,每个库有m张表, 则库表编号的计算方式为:
路由过程如下图所示:
5、分库分表的局限性和应对方案
6、怎么做 MySQL 到 ES 的数据同步
上面说到为了便于管理后台的查询,我们将订单数据冗余存储在Elasticsearch中,那么,如何在MySQL的订单数据变更后,同步到ES中呢?
这里要考虑的是数据同步的时效性和一致性、对业务代码侵入小、不影响服务本身的性能等。
其中BinLog方案比较通用,但实现起来也较为复杂,我们最终选用的是MQ方案
因为ES数据只在管理后台使用,对数据可靠性和同步实时性的要求不是特别高
考虑到宕机和消息丢失等极端情况,在后台增加了按某些条件手动同步ES数据的功能来进行补偿
滴滴ES规模参见:https://elasticsearch.cn/slides/255#page=5
ES服务架构
滴滴 ES 发展至今,承接了公司绝大部分端上文本检索、少部分日志场景和向量检索场景,包括地图 POI 检索、订单检索、客服、内搜及把脉日志 ELK 场景等。
滴滴 ES 在2020年由2.X升级到7.6.0,近几年围绕保稳定、控成本、提效能和优生态这几个方向持续探索和改进
1、架构
2、业务场景
3、部署模式
4、数据实时类同步方式
如上图所示,实时类同步方式有2种
5、成本优化
成本优化主要包括降低机器成本和降低用户成本
所以 ES 整体成本优化策略如下:
使用的 ES 版本是 7.6,而社区的最新版本已经更新至 8.13,两者之间存在约 4 年的版本差距。因此,我们今年的重点工作是将 ES 平滑升级至 8.13 版本,以解决以下问题:
小结:
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。