01 | 创建和更新订单时,如何保证数据准确无误?
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
订单系统在电商系统中扮演着重要角色,因此订单数据的准确性和完整性至关重要。为了确保数据的准确性,开发人员需要编写正确且无Bug的代码,并正确使用数据库的事务机制来确保数据操作的原子性。订单系统的核心功能包括创建订单、更新订单状态以及查询订单等,对应的数据库表包括订单主表、订单商品表、订单支付表和订单优惠表。为了实现订单服务的幂等性,可以通过为订单系统增加“生成订单号”的服务,并在用户提交订单时带上这个订单号作为主键,从而利用数据库的唯一约束特性来保证订单创建的幂等性。在处理更新订单服务时,特别需要注意ABA问题,可以通过引入版本号机制来解决。通过这样两种幂等的实现方法,可以保证订单表中的数据都是正确的。文章提供了解决ABA问题的通用方法,以及对实现服务幂等性的思考。
《后端存储实战课》,新⼈⾸单¥59
全部留言(89)
- 最新
- 精选
- 李玥置顶hello,我是李玥。之后我都会在留言板上同步上节课思考题的答案,欢迎你跟我一起学习讨论。 在课前加餐这节课里,我给你留了道思考题,让你作为公司的CTO,想一想上节课我们提到的电商系统,它的技术选型应该是什么样的? 关于这个问题,我是这么理解的。 技术选型本身没有好与坏,更多的是选择“合适”的技术。对于编程语言和技术栈的选择,我认为需要从两方面考虑,一方面就是团队的人员配置,尽量选择大家熟悉的技术,第二个方面就是要考察选择的技术它的生态是不是够完善。这两个原则在选择编程语言、技术栈、云服务和存储的时候都是适用的。 如何根据业务来选择合适的存储系统,这是个很大的话题,我会在后面的课程中陆续穿插的来和你讲,什么场景下应该选择什么样的存储,敬请期待。2020-02-26664
- 川杰请问,生成订单号 服务的一般逻辑会是怎样的?思来想去,如果要想这个ID全局唯一,只能带上时间,可是如果带上时间,像那种,不小心点了两次按钮的情况,必然是两个不同的订单号;请问这个问题怎么解决?
作者回复: 如果单纯是生成GUID(全局唯一ID)方法有很多,比如小规模系统完全可以用MySQL的Sequence或者Redis来生成。大规模系统也可以采用类似雪花算法之类的方式分布式生成GUID。 但是订单号这个东西又有点儿特殊要求,比如在订单号中最好包含一些品类、时间等信息,便于业务处理,再比如,订单号它不能是一个单纯自增的ID,否则别人很容易根据订单号计算出你大致的销量,所以订单号的生产算法在保证不重复的前提下,一般都会加入很多业务规则在里面,这个每家都不一样,算是商业秘密吧。
2020-02-262065 - 业余爱好者每次请求之前必须先生成一个唯一的请求id,服务端将该id暂时放入redis。客户端请求时必须携带上这个id,借口会首先到redis中查询,如何有的话就继续后续的处理逻辑,同时删除该id,灭有的话就退出,返回不能重复请求的错误到客户端。 一句话总结:每次处理必须对应一个一次性的token。
作者回复: 这个思路非常好,并且可以适用很多的场景。 如果是超大规模的系统,可能用一个Redis实例来生成和验证这个GUID就忙不过来了,大家也可以想一下有没有什么性能上更好的方式?
2020-02-262656 - 家庆李老师好,请问生成一个唯一订单号放在前端,如何保证客户绕过客户端直接发送请求恶意乱填订单号的问题,符合规则的订单号,而这个订单号有可能是后续系统生成的。
作者回复: 有的同学已经在留言区给出了答案,那就是,在Redis里面缓存一下给出去的订单号,收到客户端请求的时候,先验证一下这个订单号在缓存中是否存在,就可以解决这个问题了。
2020-03-06533 - 鸠摩智老师,生成全局唯一订单号如果不是自增的,插入mysql innodb表的时候,底层的B+树索引是不是会发生页分裂等问题,影响插入性能?如果遇到大促,短时间生成大量订单,写入会成为瓶颈不?
作者回复: 这确实是一个需要考虑的性能问题。 所以很多业务在设计订单号规则的时候都不是完全随机的,一般都是递增的。这种情况下,页分裂就不会特别严重。
2020-02-281429 - Sunday老师,用户手机号注册会出现多次注册现象,但因为有注销功能,我又不能设置成唯一主键,所以想问下有没有什么好的解决办法?目前是用redis锁控制并发的。还有ABA问题,一般业务中这种更新都是牵扯到更新多条,使用事务,但事务的话因为隔离级别问题,比如可重复读,感觉并发情况还是有问题
作者回复: 手机号注册重复这种情况,可以简单的用insert if not exist来解决,比如: insert into user (name, phone) select * from (select '张三', 13988888888) as tmp where not exists ( select phone from user where phone = 13988888888 ) limit 1;
2020-03-071321 - 王超老师,如果是一个预约中台的项目,只对外提供API能力,前端的预约入口应用不由自己负责,无法要求别人的前端应用在进入下单界面的时候就调用统一生成单号的服务,这种情况下有什么推荐方案么,感谢
作者回复: 这种下单接口,订单内容中一般都会有用户ID,可以通过“1秒钟内每个用户只允许最多下1单”,这种方式来判重。因为一般来说,由于重试导致的重复请求之间的时间间隔都是比较短的。 INSERT INTO my_orders (...) SELECT * FROM (SELECT 'aaa', 'bbb', 'ccc') AS tmp -- 实际要insert的数据 WHERE NOT EXISTS ( SELECT userid FROM my_orders WHERE userid = 'bbb' and last_update_time > now - 1秒 ) LIMIT 1;
2020-03-24218 - 约书亚请教您一个实际问题: 无论采用哪种生成订单id的方式,短时间内都可能出现靠前的id后提交(生成订单的问题),这样一定程度上数据库接收到的id不是严格按时间递增的。而页面浏览的场景,尤甚。 请问在您做过的系统里,这会带来一定程度上的性能下降嘛?
作者回复: 你是指在数据库中插入数据的时候,写入索引的性能下降吗?
2020-02-272318 - fgdgtz老师你好,我觉的订单号设为唯一索引约束即可,仍然有个自增主键,订单号对外用,主键不暴露,这样表空间还是连续紧密的,其他关联订单表仍然以订单号作外键
作者回复: 这样也是没问题的,很多情况下架构不是只有一种解决方案的。这种设计的好处就是主键连续,使用订单号做主键的好处是,最常用的按订单号查询会少走一次索引,各有优劣,都可以选择。
2020-03-21816 - leo关于数据报表类数据,现在主流解决方案能否推荐一下。本来想推es,但运营人员觉得操作复杂。大厂的实践是什么呢
作者回复: 后面我会专门讲,海量数据应该怎么查才会快一些。 但是,有一个原则上,底层用什么存储,应该尽量对最终用户透明,不要因为换了存储系统,就改变用户的操作习惯。
2020-03-22210