随着惟品会业务的快速发展,订单量的不断增加,原有的订单存储架构已经不能知足公司的发展了,特别是在大促高峰期,原订单库已经成为抢购瓶颈,已经严重制约公司的发展。java
惟品会旧订单库包含几十张订单相关表,旧订单库是典型的一主多从架构;主库容量已接近服务器物理空间上限,同时也已经达到 MySQL 的处理上限,很快将没法再处理新增订单。面试
旧订单库面临的问题有:数据库
一、超大容量问题缓存
订单相关表都已是超大表,最大表的数据量已是几十亿,数据库处理能力已经到了极限;服务器
单库包含多个超大表,占用的硬盘空间已经接近了服务器的硬盘极限,很快将无空间可用;架构
二、性能问题框架
单一服务器处理能力是有限的,单一订单库的 TPS 也有上限,无论如何优化,总会有达到上限,这限制了单位时间的订单处理能力,这个问题在大促时更加明显,若是不重构,订单达到必定量之后,就没法再继续增加,严重影响到用户体验。分布式
三、升级扩展问题微服务
单一主库没法灵活的进行升级和扩展,没法知足公司快速发展要求;源码分析
全部的订单数据都放在同一库里面,存在单点故障的风险;
综上所述,容量、性能问题是急需解决的问题,扩展是为了未来 3~5 年内可以很好的知足惟品会快速发展的须要,而不须要每隔几个月花费人力物力去考虑扩容等问题。
解决方法思考
一、解决容量问题
咱们能够考虑到最直接的方式是增长大容量硬盘,或者对 IO 有更高要求,还能够考虑增长 SSD 硬盘来解决容量的问题。此方法没法解决单表数据量问题。
能够对数据表历史数据进行归档,但也须要频繁进行归档操做,并且不能解决性能问题。
二、解决性能问题
提升数据库服务器的配置,这个能够提高必定数量的 QPS 和 TPS,但仍然不能解决单服务器链接数、IO 读写存在上限的问题,此方法仍然存在单点故障的问题。
拆分方法探讨
常见的数据库拆分方式有三种:垂直拆分、水平拆分、垂直水平拆分。
一、垂直拆分
垂直拆库是根据数据库里面的数据表的相关性进行拆分,好比:一个数据库里面既存在用户数据,又存在订单数据,那么垂直拆分能够把用户数据放到用户库、把订单数据放到订单库。以下图:
垂直拆表是对数据表进行垂直拆分的一种方式,常见的是把一个多字段的大表按经常使用字段和很是用字段进行拆分,每一个表里面的数据记录数通常状况下是相同的,只是字段不同,使用主键关联,以下图:
二、水平拆分
水平拆分是把单表按某个规则把数据分散到多个表的拆分方式,好比:把单表 1 亿数据按某个规则拆分,分别存储到 10 个相同结果的表,每一个表的数据是 1 千万,拆分出来的表,能够分别放至到不一样数据库中,即同时进行水平拆库操做,以下图:
水平拆分能够下降单表数据量,让每一个单表的数据量保持在必定范围内,从而提高单表读写性能。但水平拆分后,同一业务数据分布在不一样的表或库中,可能须要把单表事务改为跨表事务,须要转变数据统计方式等。
三、垂直水平拆分
垂直水平拆分,是综合了垂直和水平拆分方式的一种混合方式,垂直拆分把不一样类型的数据存储到不一样库中,再结合水平拆分,使单表数据量保持在合理范围内,提高总 TPS,提高性能,以下图:
垂直拆分策略
原订单库把全部订单相关的数据(订单销售、订单售后、订单任务处理等数据)都放在同一数据库中,不符合电商系统分层设计,对于订单销售数据,性能第一,须要可以在大促高峰承受每分钟几万到几十万订单的压力;而售后数据,是在订单生成之后,用于订单物流、订单客服等,性能压力不明显,只要保证数据的及时性便可;因此根据这种状况,把原订单库进行垂直拆分,拆分红订单售后数据、订单销售数据、其余数据等,以下图:
水平拆分策略
垂直拆分从业务上把订单下单数据与下单后处理数据分开,但对于订单销售数据,因为数据量仍然巨大,最大的订单销售相关表达到几十亿的数据量,若是遇到大型促销(如:店庆 12八、41九、61八、双十一等等),数据库 TPS 达到上限,单销售库单订单表仍然没法知足需求,还须要进一步进行拆分,在这里使用水平拆分策略。
订单分表是首先考虑的,分表的目标是保证每一个数据表的数量保持在 1000~5000 万左右,在这个量级下,数据表的大小与性能是最理想的。
若是几十个分表都放到一个订单库里面,运行于单组服务器上,则受限于单组服务器的处理能力,数据库的 TPS 有限,因此须要考虑分库,把分表放到分库里面,减轻单库的压力,增长总的订单 TPS。
一、用户编号 HASH 切分
使用用户编号哈希取模,根据数据量评估,把单库拆分红 n 个库,n 个库分别存放到 m 组服务器中,以下图:
need-to-insert-img
每组服务器容纳 4 个库,若是未来单服务器达到性能、容量等瓶颈,能够直接把数据库水平扩展为 2 倍服务器集群,还能够继续扩展为 4 倍服务器集群。水平扩展能够支撑公司在将来 3~5 年的快速订单增加。
使用用户编号进行 sharding,可使得建立订单的处理更简单,不须要进行跨库的事务处理,提升下单的性能与成功率。
二、订单号索引表
根据用户编号进行哈希分库分表,能够知足建立订单和经过用户编号维度进行查询操做的需求,可是根据统计,按订单号进行查询的占比达到 80% 以上,因此须要解决经过订单号进行订单的 CURD 等操做,因此须要创建订单号索引表。
订单号索引表是用于用户编号与订单号的对应关系表,根据订单号进行哈希取模,放到分库里面。根据订单号进行查询时,先查出订单号对应的用户编号,再根据用户编号取模查询去对应的库查询订单数据。
订单号与用户编号的关系在建立订单后是不会更改的,为了进一步提升性能,引入缓存,把订单号与用户编号的关系存放到缓存里面,减小查表操做,提高性能,索引不命中时再去查表,并把查询结果更新到缓存中。
三、分布式数据库集群
订单水平分库分表之后,经过用户编号,订单号的查询能够经过上面的方法快速定位到订单数据,但对于其余条件的查询、统计操做,没法简单作到,因此引入分布式数据库中间件。
下图是基本构架:
总结与思考
针对上面的技术我特地整理了一下,有不少技术不是靠几句话能讲清楚,因此干脆找朋友录制了一些视频,不少问题其实答案很简单,可是背后的思考和逻辑不简单,要作到知其然还要知其因此然。若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java进阶群:855801563,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。
1.具备1-5工做经验的,面对目前流行的技术不知从何下手,须要突破技术瓶颈的能够加群。
2.在公司待久了,过得很安逸,但跳槽时面试碰壁。须要在短期内进修、跳槽拿高薪的能够加群。
3.若是没有工做经验,但基础很是扎实,对java工做机制,经常使用设计思想,经常使用java开发框架掌握熟练的能够加群。
技术架构与业务场景息息相关,不能脱离实际的业务场景、历史架构、团队能力、数据体量等等去作架构重构,对于一家快速发展的电子商务公司,订单系统是核心,订单库是核心的核心,订单库的重构就像汽车在高速公路上跑着的过程当中更换轮胎。
本文是对惟品会订单库重构——采用分库分表策略对原订单库表进行拆分的粗略总结,在订单库重构过程当中遇到的问题远远超过这些,好比:历史数据的迁移、各外围系统的对接等,但这些在公司强大的技术团队面前,最终都顺利的解决,新旧订单库顺利的切换,给公司快速的业务发展提供坚实的保障。