订单设计
1 作者:kongqz
1.1 blog:http://blog.csdn.net/kongqz
2 目标
2.1 支持较大数据量
随着促销,系统订单会出现暴增
2.2 去除oracle和小型机
oracle的费用以及小型机的费用不是一般公司能承受的起,现阶段对版权授权来说是越来越严了。
2.3 支持对买家、卖家状态的实时更新
因为卖家和买家在查看自己订单的状态的时候需要知道订单的基础状态。
2.4 针对订单详细状态提供cache服务
这个cache服务,对买家和卖家同时提供,可以通过订单的id进行明细数据的提取
2.5 分布式数据存储最好对应用屏蔽
如果将规则放到客户端处理,那么对于所有的模块来说,压力太大。
3 基础知识
3.1 订单有一个整体大状态,对应买家、卖家小状态
3.2 订单与商品、买家、卖家都有关系
卖家发布商品
买家发起购买形成订单
订单对应商品
3.3 产生纠纷的时候是以订单为核心
买家与卖家的纠纷都是以订单为核心进行沟通
3.4 一个订单只能对应一个买家和一个卖家
如果一个买家购买了多个卖家的产品,那么这里必须进行订单拆分。
4 常规设计思路
4.1 直接通过程序更改业务状态
如果是关系型数据库进行单库数据存储。这样开发起来比较简单,只是完成业务逻辑即可。
4.2 单表单库操作
针对业务进行单表单库的设计,但是业务承载量有限。
4.3 做表分区
如果考虑一般的性能提升,可以针对特定字段做表分区处理提升查询效率
5 分布式设计
5.1 问题
5.1.1 买家、卖家都要查看订单
买家需要看自己购买的订单
卖家需要查看已经卖出的订单
查询都是根据自身的id进行
5.1.2 买家、卖家的相关交互核心是订单
订单可能引发一系列的操作。所以订单是整个系统交互行为的核心
5.1.3 订单的量有可能暴增
促销的时候订单量会暴增,这里相应带来数据库的磁盘IO以及存储访问能力的瓶颈。
5.1.4 单库的存储能力有限
虽然我们可以做读写分离,但是单库的存储数据量有限,访问的磁盘IO也会有限。
5.2 核心思路
通过订单号这个作为整个数据路由的中心,那么就需要买家和卖家的身影出现在以订单号为核心的相关表中。
这样也对买家和卖家的id做出要求
5.3 买家的id需要使用数字
这个是为了对订单进行取模处理。所以必须用数字
5.4 卖家的id也需要使用数字
卖家的id也是订单的一部分,为了将来分库分表进行存储做铺垫。
5.5 订单号以卖家id的一部分和卖家id的一部分拼接组成
一般情况是取得买家id(buyerid)的后三位和卖家id(sellerid)的后三位结合组装订单号,例如:
buyerid+日期+自增序列+sellerid
5.6 将订单存储在买家和卖家ID进行取模的表中
这里是根据上边的订单号进行取模,我们可以将订单号中记录的买家id的后三位和卖家id的后三位取出拼接起来,然后针对128进行取模,余多少就进入指定的库表中。
当然如果你只是想用64张表存储,那么就直接对64取模。
5.7 买家在自己的分区库中记录订单id和订单的状态
需要冗余这么一张单独的小表
buyerid:买家id
orderno:订单号
order_status:订单大状态,可以推算出订单的买家、卖家状态
5.8 卖家在自己对应的分区库中记录订单id和订单的状态
需要冗余这么一张单独的小表
sellerid:卖家id
orderno:订单号
order_status:订单大状态,可以推算出订单的买家、卖家状态
5.9 买家、卖家更新状态的时候通过消息通知买家订单和卖家订单状态
无论买家和卖家谁进行订单状态的处理,都需要对订单在三个地方的状态进行处理
这个如果数据量不大可以通过消息服务器activemq进行。
如果订单数据量巨大,那么最好通过专业的消息服务器例如jmetaq进行数据可靠性处理。
6 实施技术
6.1 可使用tddl抽象数据处理对应用透明
tddl复杂度相对高一些。如果无特殊需求,不推荐使用。
6.2 可使用cobar服务器端规则来进行数据拆分储存
这个可以参考阿里的cobar项目的规则定制
6.3 针对3个月以上的订单进行历史归档
这个可以在对应的分库上进行
6.4 针对历史订单的读可以走从库
历史订单是已经没有可能发生状态改变,所有读的操作可以走从库
对于一般对数据实时性要求不高的操作,例如搜索引擎加载,都可以通过从库进行
BTW:有图有真相