1、问题描述
平台直播时,遇到超买情况,即使事务中查库存加了for update锁,仍然出现超买情况。超买:库存剩余2个,生成订单多于2笔
2、排查问题
(1)并发压测
使用ab工具并发压测
ab -c 100 -n 100 xxx
(2)事务相关报错
记录错误日志有报错,貌似事务不存在
[42000]SQLSTATE[42000]: Syntax error or access violation: 1305 SAVEPOINT trans2 does not exist[/data/www_code/dev/zz/smart_fenxiao/smart_crmeb/vendor/topthink/think-orm/src/db/PDOConnection.php:1402]
网上搜索出现原因,解决方案,很少解答,有英文解答,大致原因是事务嵌套导致的,找不到事务提交或者回滚节点。
检查代码,发现业务层加了事务,手动事务内部,涉及多个model更新插入数据部分,TP6框架底层Model新增,更新数据方法用到了事务。
猜测1,可能是在model层有事务提交,导致整个事务提交,业务层加的事务自动提交则会找不到事务节点。
猜测2, 有事务嵌套,事务内部有报错,回滚代码找不到对应事务
解决:使用DB的插入更新数据代替model的使用(注:model层的方法也是使用DB方式,只不过方法中又加了事务逻辑,model层insert 相关方法直接是继承DB的,可直接用model调用,update被model层方法重写,不可直接使用)
(3)并发死锁报错
上述问题解决后,继续压测,有SQL报错
SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction
经过记录执行日志,排查到产生死锁的位置,生成订单记录时,订单表加了唯一索引,逻辑原因,并发产生字段不唯一性,导致产生死锁
解决:数据库唯一索引去掉,因为逻辑中足以保证字段唯一性
3、总结
(1)结果
最终压测超买问题没有出现。验证:去掉for update 锁,压测会出现多于库存的订单
(2)排查问题总结
- thinkphp6中model的creaete,update相关方法有坑啊,竟然有隐藏事务
- 排查问题,多打日志,异常日志等,特别是压测时,不会有直接结果输出,应记录日志观察数据结果
- 并发测试,库存100,ab测试,
-c 100 -n 100
并发100,完成请求数100,而会有部分请求失败(挺多),估计是唯一索引导致的失败,而事务会回滚,因为事务嵌套,导致事务保存点丢失报错。解决了几个问题后,没有出现超买问题,应该与所述问题有关系,至于详细原因,待分析排查。 - 并发测试工具可以用ab, wrk(网上说wrk性能更好点)