需求:将投资的钱拆成几份随机分配给借款人。 起初业务程序思路是这样的: 投资人投资后,将金额随机分为几份,然后随机从借款人表里面选几个,然后通过一条条 select for update 去更新借款人表里面的余额等。 例如两个用户同时投资,A 用户金额随机分为 2 份,分给借款人 1,2, B 用户金额随机分为 2 份,分给借款人 2,1。 由于加锁的顺序不一样,死锁当然很快就出现了。 对于这个问题的改进很简单,直接把所有分配到的借款人直接一次锁住就行了。
Select * from xxx whereidin (xx,xx,xx) forupdate
在 in 里面的列表值 mysql 是会自动从小到大排序,加锁也是一条条从小到大加的锁
例如(以下会话id为主键):
Session1: mysql> select * from t3 where id in (8,9) for update; +----+--------+------+---------------------+ | id | course | name | ctime | +----+--------+------+---------------------+ |8| WA | f | 2016-03-02 11:36:30 | | 9 | JX | f |2016-03-0111:36:30| +----+--------+------+---------------------+ rows in set (0.04 sec) Session2: select * from t3 where id in (10,8,5) for update;
Session3: mysql> select * from t3 where id=5for update;
锁等待中
Session4: mysql> select * from t3 where id=10for update; +----+--------+------+---------------------+ | id | course | name | ctime | +----+--------+------+---------------------+ |10| JB | g | 2016-03-10 11:45:05 | +----+--------+------+---------------------+ row in set (0.00 sec)
在其它session中id=5是加不了锁的,但是id=10是可以加上锁的。
案例二
在开发中,经常会做这类的判断需求:根据字段值查询(有索引),如果不存在,则插入;否则更新。
以id为主键为例,目前还没有id=22的行
Session1: select * from t3 whereid=22forupdate; Empty set (0.00 sec)
session2: select * from t3 whereid=23forupdate; Empty set (0.00 sec)
Session1: insertinto t3 values(22,'ac','a',now()); 锁等待中……
Session2: insertinto t3 values(23,'bc','b',now()); ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
锁住的范围为: 无穷小或小于表中锁住id的最大值,无穷大或大于表中锁住id的最小值, 如: 如果表中目前有已有的id为(11 , 12), 那么就锁住(12,无穷大)。 如果表中目前已有的id为(11 , 30), 那么就锁住(11,30) 对于这种死锁的解决办法是: insert into t3(xx,xx) on duplicate key update xx ='XX'; 用mysql特有的语法来解决此问题。因为insert语句对于主键来说,插入的行不管有没有存在,都会只有行锁
案例三
mysql> select * from t3 where id=9for update; +----+--------+------+---------------------+ | id | course | name | ctime | +----+--------+------+---------------------+ | 9 | JX | f | 2016-03-0111:36:30 | +----+--------+------+---------------------+
row inset (0.00 sec) Session2: mysql> select * from t3 where id<20 for update; 锁等待中
Session1: mysql> insert into t3 values(7,'ae','a',now()); ERROR 1213 (40001): Deadlock found when trying to getlock; try restarting transaction
deletefrom dltask where a=’a’ and b=’b’ and c=’c’;
并且产生了以下的并发执行逻辑,就会产生死锁: 上面分析的这个并发流程,完整展现了死锁日志中的死锁产生的原因。其实,根据事务1步骤6,与事务0步骤3/4之间的顺序不同,死锁日志中还有可能产生另外一种情况,那就是事务1等待的锁模式为记录上的X锁 + No Gap锁(lock_mode X locks rec but not gap waiting)。这第二种情况,也是”润洁”同学给出的死锁用例中,使用MySQL 5.6.15版本测试出来的死锁产生的原因。 此类死锁,产生的几个前提: