行级锁在使用的时候并不是直接锁掉这行记录,而是锁索引
如果一条sql用到了主键索引(mysql主键自带索引),mysql会锁住主键索引;
如果一条sql操作了非主键索引,mysql会先锁住非主键索引,再锁定主键索引.
以下问题的总结:同一个事务,两次修改同一个表,第一个sql先锁A索引再锁B索引,第二个sql先锁B索引再锁A索引,而这两个SQL的数据应该是有重叠
CREATE TABLE user_item
(
id
BIGINT(20) NOT NULL,
user_id
BIGINT(20) NOT NULL,
item_id
BIGINT(20) NOT NULL,
status
TINYINT(4) NOT NULL,
PRIMARY KEY (id
),
KEY idx_1
(user_id
,item_id
,status
)
) ENGINE=INNODB DEFAULT CHARSET=utf-8
update user_item set status=1 where user_id=? and item_id=?
1.由于用到了非主键索引,首先需要获取idx_1上的行级锁
2.紧接着根据主键进行更新,所以需要获取主键上的行级锁;
3.更新完毕后,提交,并释放所有锁。
//如果在步骤1和2之间突然插入一条语句:
update user_item .....where id=? and user_id=?
//这条语句会先锁住主键索引,然后锁住idx_1。
//蛋疼的情况出现了,一条语句获取了idx_1上的锁,等待主键索引上的锁;
//另一条语句获取了主键上的锁,等待idx_1上的锁,这样就出现了死锁。
解决方案
//1.先获取需要更新的记录的主键
select id from user_item where user_id=? and item_id=?
//2. 逐条更新
...