创建订单和更新订单的数据一致性问题
大家好,我是Leo。
聊一下创建订单和更新订单的数据一致性问题,文章分类主要是MySQL ,Redis,秒杀系统,RocketMQ,计算机网络,大厂面试,设计模式,Nginx。先整理一下。方便粉丝更好的阅读,同时也方便自己不断的复习沉淀。
重复下单
我们在下单时,往往会因为网络问题出现多次下单的情况,比如点了下单一直没反应,我们就会多次的重复点击,如果服务端做了校验可能不会出现什么问题,如果没做的话在订单列表里可能就会出现多个订单。
解决方案就是我们可以对订单实现具备幂等性。
幂等性就是无论点击多少次下单,始终只会创建一条记录。
如果系统体量比较大的话,我们可以独立一个生成订单号服务,体量不大的话可以封装成一个API给订单服务使用。
当用户从购物车界面点结算挑战到订单详情界面就请求一次生成订单号,使这个订单详情页的缓存中保存一条唯一的订单号 只要每次下单请求的订单号是唯一的,我们再借助数据库中主键唯一约束性来实现,订单数据的唯一性。
如果主键是订单号的话,主键自动帮我们实现了。如果主键是时间戳ID的话,我们把订单号字段设为唯一索引,同时也可以避免重复下单的校验需求。
ABA问题
订单服务会经常出现ABA问题。
什么是 ABA 问题呢?我们举个例子,订单支付之后,小二要发货,发货完成后要填个快递单号。假如小二填了一个单号 666,刚填完,发现填错了,赶紧再修改成 888。对订单服务来说,这就是 2 个更新订单的请求。
正常情况下,订单中的快递单号会先更新成 666,再更新成 888,这是没问题的。那不正常情况呢?666 请求到了,单号更新成 666,然后 888 请求到了,单号又更新成 888,但是 666 更新成功的响应丢了,调用方没收到成功响应,自动重试,再次发起 666 请求,单号又被更新成 666 了,这数据显然就错了。这就是ABA 问题。
解决方案就是我们在订单表中加一个版本号这个字段。
在查询订单时,我们可以把版本号返回给前端,前端在处理下单时,以参数的形式传递给后端,后端收到版本号之后与数据库的实际数据对比,如果版本号符合,修改数据,版本号递增。
为了考虑数据安全性,我们一般会将 校验版本号,修改数据,修改版本号在一个事务中执行
UPDATE orders set tracking_number = 666, version = version + 1
WHERE version = 8;
通过版本号,我们就可以得知,在每次修改数据时,是否有其他人修改过。这样就不会出现ABA问题了。