记一次MySQL获取锁失败以及使用`SecureRandom 生成随机数引起的阻塞问题

上午测试甩群里一个问题让我看,手机扫码支付成功后,但是一直显示支付二维码页面,页面没跳转

看来下日志有报错:

Lock wait timeout exceeded; try restarting transaction

报错很明确:超过了锁定等待超时;尝试重新启动事务

业务逻辑大致这样,用户购买课程后,判断用户支付成功会修改下该课程的学习人数,但是现在执行update的时候卡住了,因为尝试获取锁,但是锁被其他线程(事务)拿去了,但是还没释放锁,所以后面的线程获取不到锁执行等待,直到超时失败。

这个问题又引起了一个现象就是点主页或者其他页面的时候页面打不开,因为这些页面查询的sql都涉及到上面update的表,按说MySQL的InnoDB是行级锁,不应该把整表锁住,看了下表的索引,发现是没有索引的,InnoDB的行级锁是基于索引的,如果没有索引则加表锁,所以把update时where条件字段设置为索引,解决了其他页面打不开的问题。

回到update获取锁失败的问题上,我以为只要看下当前有哪些正在执行的sql锁住了,把其中的锁释放掉就可以了,于是用到下面这些语句:

-- 查询当前运行的所有事务
SELECT * FROM information_schema.innodb_trx;
-- 查询等待锁的事务
SELECT * FROM information_schema.innodb_lock_waits;   
-- 查询出现的锁
SELECT * FROM information_schema.INNODB_LOCKs
-- 查询mysql数据库中存在的进程
select * from information_schema.PROCESSLIST

通过上面的命令能清晰的看到哪些sql卡住了正在等待哪个线程释放锁资源,然后我手动kill + pid 来解决,即使手动释放锁的方式并不行,因为前端查询订单状态的接口一直刷,kill之后下个线程过来了还会卡死,于是我花了好长时间在尝试怎么解决这个问题,最后还是没招了,因为找不到原因为啥uptade的时候一定会获取锁失败。

后来逐行看日志,发现第一个进来的线程突然走到一半卡住了,但是update语句执行成功了,然后还释放了分布式锁(查询订单支付状态先加了分布式锁),但是事务还没提交,当下个线程来的时候进入update会卡住,因为上个线程没有提交事务释放锁,现在终于找到原因了,就是因为第一个线程进来卡在了生成随机数的代码上

Random rand = SecureRandom.getInstanceStrong();

通过上面的代码生成随机数,之前是 Random rand = new Random();是没问题的,后来代码检测工具提示这么写不好,不要每次进到方法都创建随机对象,于是根据建议改成上面的方式,没想到改成了上面的方式之后造成了线程阻塞,这在我windows机器上启动是没问题的,但是部署到linux就不行了,后来查了下SecureRandom.getInstanceStrong();确实在Linux上能引起线程阻塞,需要修改启动参数就好了,有点麻烦没采用,恢复了之前的生成随机数方式 new Random(),最后终于把问题解决。

SecureRandom引起阻塞原因参考:

https://blog.csdn.net/iteye_11910/article/details/82655175

最后修改:2020 年 07 月 14 日 04 : 43 PM