一、配景随着用户量级的快速增长,vivo 官方商城 v1.0 的单体架构逐渐袒露出毛病:模块愈发臃肿、开发效率低下、性能泛起瓶颈、系统维护难题。从2017年开始启动的 v2.0 架构升级,基于业务模块举行垂直的系统物理拆分,拆分出来业务线各司其职,提供服务化的能力,配合支撑主站业务。订单模块是电商系统的生意业务焦点,不停累积的数据即将到达单表存储瓶颈,系统难以支撑新品公布和大促运动期间的流量,服务化革新势在必行。
本文将先容 vivo 商城订单系统建设的历程中遇到的问题息争决方案,分享架构设计履历。二、系统架构将订单模块从商城拆分出来,独立为订单系统,使用独立的数据库,为商城相关系统提供订单、支付、物流、售后等尺度化服务。系统架构如下图所示:三、技术挑战1、数据量和高并发问题首先面临的挑战来自存储系统:1、数据量问题随着历史订单不停累积,MySQL中订单表数据量已达千万级。我们知道InnoDB存储引擎的存储结构是B+树,查找时间庞大度是O(log n),因此当数据总量n变大时,检索速度一定会变慢, 岂论如何加索引或者优化都无法解决,只能想措施减小单表数据量。
数据量大的解决方案有:数据归档、分表。2、高并发问题商城业务处于高速生长期,下单量屡创新高,业务庞大度也在提升,应用法式对MySQL的会见量越来越高。
单机MySQL的处置惩罚能力是有限的,当压力过大时,所有请求的会见速度都市下降,甚至有可能使数据库宕机。并发量高的解决方案有:使用缓存、读写分散、分库。下面临这些方案举行简朴形貌:1)数据归档订单数据具备时间属性,存在热尾效应,大部门情况下检索的都是最近的订单,而订单内外却存储了大量使用频率较低的老数据。
那么就可以将新老数据离开存储,将历史订单移入另一张表中,并对代码中的查询模块做一些相应改动,便能有效解决数据量大的问题。2)使用缓存使用Redis作为MySQL的前置缓存,可以盖住大部门的查询请求,并降低响应时延。缓存对商品系统这类与用户关系不大的系统效果特别好,但对订单系统而言,每个用户的订单数据都纷歧样,缓存掷中率不算高,效果不是太好。
3)读写分散主库卖力执行数据更新请求,然后将数据变换实时同步到所有从库,用多个从库来分管查询请求。但订单数据的更新操作较多,下单岑岭时主库的压力依然没有获得解决。且存在主从同步延迟,正常情况下延迟很是小,不凌驾1ms,但也会导致在某一个时刻的主从数据纷歧致。
那就需要对所有受影响的业务场景举行兼容处置惩罚,可能会做一些妥协,好比下单乐成后先跳转到一个下单乐成页,用户手动点击检察订单后才气看到这笔订单。4)分库分库又包罗垂直分库和水平分库:水平分库:把同一个表的数据按一定规则拆到差别的数据库中,每个库可以放在差别的服务器上;垂直分库:根据业务将表举行分类,漫衍到差别的数据库上面,每个库可以放在差别的服务器上,它的焦点理念是专库专用。
5)分表分表又包罗垂直分表和水平分表:水平分表:在同一个数据库内,把一个表的数据按一定规则拆到多个表中;垂直分表:将一个表根据字段分成多表,每个表存储其中一部门字段。我们综合思量了革新成本、效果和对现有业务的影响,决议直接使用最后一招:分库分表。2、分库分表技术选型分库分表的技术选型主要从这几个偏向思量:客户端sdk开源方案中间件proxy开源方案公司中间件团队提供的自研框架自己动手造轮子参考之前项目履历,并与公司中间件团队相同后,接纳了开源的 Sharding-JDBC 方案。
现已更名为Sharding-Sphere。Github:https://github.com/sharding-sphere/文档:官方文档比力粗拙,可是网上资料、源码剖析、demo比力富厚社区:活跃特点:jar包方式提供,属于client端分片,支持xa事务1)分库分表计谋联合业务特性,选取用户标识作为分片键,通过盘算用户标识的哈希值再取模来获得用户订单数据的库表编号。假设共有n个库,每个库有m张表,则库表编号的盘算方式为:- 库序号:Hash(userId) / m % n- 表序号:Hash(userId) % m路由历程如下图所示:2)分库分表的局限性和应对方案分库分表解决了数据量和并发问题,但它会极大限制数据库的查询能力,有一些之前很简朴的关联查询,在分库分表之后可能就没法实现了,那就需要单独对这些Sharding-JDBC不支持的SQL举行改写。
除此之外,还遇到了这些挑战:①全局唯一ID设计分库分表后,数据库自增主键不再全局唯一,不能作为订单号来使用,但许多内部系统间的交互接口只有订单号,没有用户标识这个分片键,如何用订单号来找到对应的库表呢?原来,我们在生成订单号时,就将库表编号隐含在其中了。这样就能在没有用户标识的场景下,从订单号中获取库表编号。②历史订单号没有隐含库表信息用一张表单独存储历史订单号和用户标识的映射关系,随着时间推移,这些订单逐渐不在系统间交互,就逐步不再被用到。
③治理后台需要凭据种种筛选条件,分页查询所有满足条件的订单将订单数据冗余存储在搜索引擎Elasticsearch中,仅用于后台查询。3、怎么做 MySQL 到 ES 的数据同步上面说到为了便于治理后台的查询,我们将订单数据冗余存储在Elasticsearch中,那么,如何在MySQL的订单数据变换后,同步到ES中呢?这里要思量的是数据同步的时效性和一致性、对业务代码侵入小、不影响服务自己的性能等。
1)MQ方案ES更新服务作为消费者,吸收订单变换MQ消息后对ES举行更新2)Binlog方案ES更新服务借助canal等开源项目,把自己伪装成MySQL的从节点,吸收Binlog并剖析获得实时的数据变换信息,然后凭据这个变换信息去更新ES。其中BinLog方案比力通用,但实现起来也较为庞大,我们最终选用的是MQ方案。
因为ES数据只在治理后台使用,对数据可靠性和同步实时性的要求不是特别高。思量到宕机和消息丢失等极端情况,在后台增加了按某些条件手动同步ES数据的功效来举行赔偿。4、如何宁静地更换数据库如何将数据从原来的单实例数据库迁移到新的数据库集群,也是一大技术挑战。不光要确保数据的正确性,还要保证每执行一个步骤后,一旦泛起问题,能快速地回滚到上一个步骤。
我们思量了停机迁移和不停机迁移的两种方案:1)不停机迁移方案:把旧库的数据复制到新库中,上线一个同步法式,使用 Binlog等方案实时同步旧库数据到新库;上线双写订单新旧库服务,只读写旧库;开启双写,同时停止同步法式,开启对比赔偿法式,确保新库数据和旧库一致;逐步将读请求切到新库上;读写都切换到新库上,对比赔偿法式确保旧库数据和新库一致;下线旧库,下线订单双写功效,下线同步法式和对比赔偿法式。2)停机迁移方案:上线新订单系统,执行迁移法式将两个月之前的订单同步到新库,并对数据举行考核;将商城V1应用停机,确保旧库数据不再变化;执行迁移法式,将第一步未迁移的订单同步到新库并举行考核;上线商城V2应用,开始测试验证,如果失败则回退到商城V1应用(新订单系统有双写旧库的开关)。思量到不停机方案的革新成本较高,而夜间停机方案的业务损失并不大,最终选用的是停机迁移方案。
5、漫衍式事务问题电商的生意业务流程中,漫衍式事务是一个经典问题,好比:用户支付乐成后,需要通知发货系统给用户发货;用户确认收货后,需要通知积分系统给用户发放购物奖励的积分。我们是如何保证微服务架构下数据的一致性呢?差别业务场景对数据一致性的要求差别,业界的主流方案中,用于解决强一致性的有两阶段提交(2PC)、三阶段提交(3PC),解决最终一致性的有TCC、当地消息、事务消息和最大努力通知等。这里差池上述方案举行详细的形貌,先容一下我们正在使用的当地消息表方案:在当地事务中将要执行的异步操作记载在消息表中,如果执行失败,可以通过定时任务来赔偿。
下图以订单完成后通知积分系统赠送积分为例。6、系统宁静和稳定性1)网络隔离只有少少数第三方接口可通过外网会见,且都市验证签名,内部系统交互使用内网域名和RPC接口。2)并发锁任何订单更新操作之前,会通过数据库行级锁加以限制,防止泛起并发更新。
3)幂等性所有接口均具备幂等性,不用担忧对方网络超时重试所造成的影响。4)熔断使用Hystrix组件,对外部系统的实时挪用添加熔断掩护,防止某个系统故障的影响扩大到整个漫衍式系统中。5)监控和告警通过设置日志平台的错误日志报警、挪用链的服务分析告警,再加上公司各中间件和基础组件的监控诉警功效,让我们能够能够第一时间发现系统异常。7、踩过的坑1)接纳MQ消费的方式同步数据库的订单相关数据到ES中,遇到的写入数据不是订单最新数据问题。
下图左边是原方案:在消费订单数据同步的MQ时,如果线程A在先执行,查出数据,这时候订单数据被更新了,线程B开始执行同步操作,查出订单数据后先于线程A一步写入ES中,线程A执行写入时就会将线程B写入的数据笼罩,导致ES中的订单数据不是最新的。解决方案是在查询订单数据时加行锁,整个业务执行在事务中,执行完成后再执行下一个线程。2)sharding-jdbc 分组后排序分页查询出所有数据问题示例:select a from temp group by a,b order by a desc limit 1,10。
执行是Sharding-jdbc里group by 和 order by 字段温顺序纷歧致是将10置为Integer.MAX_VALUE, 导致分页查询失效。io.shardingsphere.core.routing.router.sharding.ParsingSQLRouter#processLimitprivate void processLimit(final List<Object> parameters, final SelectStatement selectStatement, final boolean isSingleRouting) { boolean isNeedFetchAll = (!selectStatement.getGroupByItems().isEmpty() || !selectStatement.getAggregationSelectItems().isEmpty()) && !selectStatement.isSameGroupByAndOrderByItems(); selectStatement.getLimit().processParameters(parameters, isNeedFetchAll, databaseType, isSingleRouting);}io.shardingsphere.core.parsing.parser.context.limit.Limit#processParameters/*** Fill parameters for rewrite limit.** @param parameters parameters* @param isFetchAll is fetch all data or not* @param databaseType database type* @param isSingleRouting is single routing or not*/public void processParameters(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType, final boolean isSingleRouting) { fill(parameters); rewrite(parameters, isFetchAll, databaseType, isSingleRouting);}private void rewrite(final List<Object> parameters, final boolean isFetchAll, final DatabaseType databaseType, final boolean isSingleRouting) { int rewriteOffset = 0; int rewriteRowCount; if (isFetchAll) { rewriteRowCount = Integer.MAX_VALUE; } else if (isNeedRewriteRowCount(databaseType) && !isSingleRouting) { rewriteRowCount = null == rowCount ? -1 : getOffsetValue() + rowCount.getValue(); } else { rewriteRowCount = rowCount.getValue(); } if (null != offset && offset.getIndex() > -1 && !isSingleRouting) { parameters.set(offset.getIndex(), rewriteOffset); } if (null != rowCount && rowCount.getIndex() > -1) { parameters.set(rowCount.getIndex(), rewriteRowCount); }}正确的写法应该是 select a from temp group by a desc ,b limit 1,10使用的版本是sharing-jdbc的3.1.1。ES分页查询如果排序字段存在重复的值,最好加一个唯一的字段作为第二排序条件,制止分页查询时遗漏数据、查出重复数据,好比用的是订单建立时间作为唯一排序条件,同一时间如果存在许多数据,就会导致查询的订单存在遗漏或重复,需要增加一个唯一值作为第二排序条件或者直接使用唯一值作为排序条件。
四、结果一次性上线乐成,稳定运行了一年多;焦点服务性能提升十倍以上;系统解耦,迭代效率大幅提升;能够支撑商城至少五年的高速生长。五、结语我们在系统设计时并没有一味追求前沿技术和思想,面临问题时也不是直接接纳主流电商的解决方案,而是凭据业务实际状况来选取最合适的措施。小我私家以为,一个好的系统不是在一开始就被大牛设计出来的,一定是随着业务的生长和演进逐渐被迭代出来的,连续预判业务生长偏向,提前制定架构演进方案,简朴来说就是:走到业务的前面去!作者丨官网商城开发团队泉源丨vivo互联网技术(ID:vivoVMIC)dbaplus社群接待宽大技术人员投稿,投稿邮箱:editor@dbaplus.cn。
本文关键词:pg电子,系统,架构,革新,实操,从,不堪重负,到,10倍,一
本文来源:pg电子-www.mypadrentals.com