记一次生产环境mysql死锁

mysql-binlog

这是Mysql的binlog日志,其中,third_logistics_no、order_no都应该是字符串类型,但是这里执行的时候变成了数值型;

业务逻辑是:程序在进来的时候首先会锁住该订单号的物流记录,然后执行更新语句;

然而在执行更新语句的时候,本来应该是字符串类型的参数却变成了数值型,给表列赋值与表类型不符合时,MySql底层的优化器发挥作用,会做一个强制类型转化,此时能正常操作,但会导致行锁升级为表锁;

重现场景如下:

session1:begin;

session2:begin;

session1:select * from orders where order_no = '001' for update;

session2:select * from orders where order_no = '002' for update;

session1:update orders set order_status = 1 where order_no = 001;// session2上面已经获取了002的锁,产生了锁等待,因为MySQL强制类型转换,行锁变成了表锁;

session2:update orders set order_status = 2 where order_no = 002;// 执行就报了死锁;

总结:session1等session2的锁,session2等session1的锁,同时你等我的锁,我等你的锁,然后就死锁了;
session1

session2

导致这个结果的原因是因为,sql语句的拼接中使用的是字符串拼接,写法为:s""" update orders set order_no = ${request} """ 这个写法
总结此次教训,做了两个实验,如下:


image.png

可以看出,虽然我们定义的参数都是字符串类型,但是由于我们本身的写法就是scala的字符串拼接,位于scala-lang包下的StringContext,在字符串处理的过程中,这个空串和引号直接被去掉了,得出的sql语句语法上就是错误的


image.png

正确的写法应该是用scala-sql,同样的参数,也是字符串类型,语句和参数应该是这样分开做两次封装的,传入的参数是什么类型,执行的语句中参数就是什么类型;

综上,执行的sql语句要严格按照标准规范来写,不同的东西效果肯定不一样。

你可能感兴趣的:(记一次生产环境mysql死锁)