后端存储实战一

订单系统的核心功能和数据

必备的功能,包括但不限于如下:

1.创建订单

2.随着购物流程更新订单状态

3.查询订单,包括用订单数据生成各种报表

为了支撑这些必备功能,在数据库中,我们至少需要有这样几张表

1.订单主表:也叫订单表,保存订单的基本信息

2.订单商品表:保存订单中的商品信息

3.订单支付表:保存订单的支付和退款信息

4.订单优惠表:保存订单使用的所有优惠信息

几个表之间的关系如下:

订单主表和后面的几个子表都是一对多的关系,关联的外键就是订单主表的主键,也就是订单号。

如何避免重复下单?

保证自己的下单接口是幂等的。

具体实现如下,进入订单页面时,前端请求后端获取一个订单号,后端将订单号设为唯一索引,注意不要使用订单号当做主键。

这样可以利用唯一约束,前端带着订单号,重复下单会插入失败。时序流程图如下

后端存储实战一_第1张图片

值得注意的一点是,插入冲突了,不要报错给前端,返回成功就可以了。

为啥不用订单号来做主键?

生成全局唯一订单号如果不是自增的,插入mysql innodb表的时候,底层的B+树索引是不是会发生页分裂等问题,影响插入性能,如果遇到大促,短时间生成大量订单,写入会成为瓶颈。

所以主键使用db的默认的自增主键比较好,不过牺牲是会多建个索引

如果要使用订单号当做主键,不用额外建个唯一索引,另外很多业务在设计订单号规则的时候都不是完全随机的,一般都是递增的。这种情况下,页分裂就不会特别严重。

总结:各有利弊,个人比较喜欢不用订单号做主键

生成一个唯一订单号放在前端,如何保证客户绕过客户端直接发送请求恶意乱填订单号的问题,符合规则的订单号,而这个订单号有可能是后续系统生成的

1.生成的时候在redis中set下,下单时查询,不再里面的都拒绝

2.生成订单号加些自己的东西,什么secret等等,总而言之,别人看不出你的订单号生成规律,不好模仿或者无法模仿,而你自己可以通过订单号解析出来是不是自己后端生成的(不依赖存储,类似于jwt的生成和认证)

ABA问题

一句话总结就是,2个更新请求,先发出的请求因为网络原因导致比后发出的请求到来的慢,从而把最新修改的值又更到旧版本了。

举个例子:

订单支付之后,小二要发货,发货完成后要填个快递单号。假设说,小二填了一个单号 666,刚填完,发现填错了,赶紧再修改成 888。对订单服务来说,这就是 2 个更新订单的请求。

正常情况下,订单中的快递单号会先更新成 666,再更新成 888,这是没问题的。那不正常情况呢?666 请求到了,单号更新成 666,然后 888 请求到了,单号又更新成 888,但是 666 更新成功的响应丢了,调用方没收到成功响应,自动重试,再次发起 666 请求,单号又被更新成 666 了,这数据显然就错了。这就是非常有名的 ABA 问题

解决方法:使用版本号

给你的订单主表增加一列,列名可以叫 version,也即是“版本号”的意思。每次查询订单的时候,版本号需要随着订单数据返回给页面。页面在更新数据的请求中,需要把这个版本号作为更新请求的参数,再带回给订单更新服务。订单服务在更新数据的时候,需要比较订单当前数据的版本号,是否和消息中的版本号一致,如果不一致就拒绝更新数据。如果版本号一致,还需要再更新数据的同时,把版本号 +1。“比较版本号、更新数据和版本号 +1”,这个过程必须在同一个事务里面执行。具体的 SQL 可以这样来写:

UPDATE orders set tracking_number = 666, version = version + 1 WHERE version = 8 and order_id = 123456;

在这条 SQL 的 WHERE 条件中,version 的值需要页面在更新的时候通过请求传进来。通过这个版本号,就可以保证,从我打开这条订单记录开始,一直到我更新这条订单记录成功,这个期间没有其他人修改过这条订单数据。因为,如果有其他人修改过,数据库中的版本号就会改变,那我的更新操作就不会执行成功。我只能重新查询新版本的订单数据,然后再尝试更新。

 

 

 

 

你可能感兴趣的:(后端那些事儿)