一年前,在上一家公司接手了一个含有订单系统的项目,业务并不复杂,但是当时令我比较困惑的是订单表的设计。
困惑的点主要是随着订单量增加,单表的存储能力将达到瓶颈,必然要采用分表的方案,那么按照什么维度拆分合适呢?
分表之后带来的最大的挑战是订单查询。如果以用户为中心,采用userId取模,可以很方便的处理用户查单诉求,但是对商家或产品而言,数据就比较分散,相反如果以商家或商品为中心,则用户查单比较困难。
由于对这样的业务没有成熟的经验,最后只能采取相对讨巧的方案,按照自然日期分表,考虑订单量并不大,当时是每个季度一张表,一年4张表。
为什么说讨巧?以用户查单为例,查询页默认要选择一个季度,也就是将用户订单数据限制在一张表中做查询。大多数时候用户也只是查看最近订单,所以勉强可以接受。
如果用户一定要查看全部订单,只能将所有订单表union all,当时想的是至少前几个年头订单表数量有限,性能问题不会太严重,以后再想办法进行优化。
前几天又想到了这个订单表的设计问题,于是和现在的同事进行了简单的沟通,也学到了非常宝贵的经验。在这里简单记录一下,说的不对的地方希望大家多多包涵并不吝赐教。
具体怎么做呢?
简单来说就是冗余+同步+检查。
数据冗余,非常有效的解决了数据分散的问题。将订单落库订单表(暂且称作订单表O),订单表按照日期分表,按天,周,月,季度或者年都可以,可根据实际业务量选择。分表首先解决了单表存储的瓶颈问题。如何冗余?以用户查单为例,新建用户订单表(订单表U-O),将userId取模分表,冗余一份订单数据,U-O表中的数据可以只包含重要信息如下单时间,数量,金额,订单状态等(列表页展示)。订单详情信息可以从O表中查询。
这样一来,用户查单自然就只限一张表中啦。这么简单的问题为什么我没想到呢?不过这样一来,新的问题出现了,就是如何保证这两份(也可能多份)订单表数据同步?虽然订单的大部分信息不会经常变更,但是在订单完成之前,必然有订单状态的变更或用户更新的可能。这就是我将要说的数据同步问题。
如何同步?可能大家会想到将几份订单数据放入事务处理,虽然事务可以有效的解决同步问题(事务一致性),但是我认为事务一方面影响了数据库性能,另一方便增加了接口复杂性(耦合)。同事给我的方案是通过消息组件同步,消息在分布式系统中是非常重要的存在,可以很好的将服务解耦,而且高可用,支持高并发,响应速度快。
但是消息同步还不能保证数据同步的绝对一致,比如消息延时,重复,丢失等。一方面要优化消息组件的稳定性,另一方面要做好监控。最重要的是要采取一个主动检查的手段。
做好数据检查。比如启一个job每天凌晨进行一次前一天的数据检查,比对所有订单的一致性。既然采用了消息机制,我们所有的对订单的更新操作都是对O表进行的,因此以O表数据为准即可。
以上就是同事那里收获到的比较不错的方案,因为没有深入剖析,加之我所说的比较肤浅,如果能亲自参与一次实际的开发,也许我会跟大家分享更多有价值的东西。