Loading

mysql并发插入死锁

前言

开发中遇到多线程并发情况对数据批量插入

主键id非雪花id,函数自增id

原理分析

两个事务都持有该行的 S 锁,期望获取 X 锁时被对方阻塞了。

通俗讲就是并发插入出现相同的主键id导致阻塞

在insert操作的时候就需要去拿 X 锁

共享锁(Shared Lock)

共享锁(Shared Lock),又称S锁、读锁。针对行锁。
当有事务对数据加读锁后,其他事务只能对锁定的数据加读锁,不能加写锁(排他锁),所以其他事务只能读,不能写。

📢主要为了支持并发读的场景,读时不允许写操作。

加锁方式:
select * from T where id=1 lock in share mode;
释放方式:
commit、rollback;

排他锁(EXclusive Lock)

排他锁(EXclusive Lock),又称X锁、独占锁、写锁。针对行锁。
当有事务对数据加写锁后,其他事务不能再对锁定的数据加任何锁,又因为InnoDB对select语句默认不加锁,所以其他事务除了不能写操作外,照样是允许读的(尽管不允许加读锁)。

📢主要为了在事务进行写操作时,不允许其他事务修改。

加锁方式:
自动:DML语句默认加写锁
手动:select * from T where id=1 for update;
释放方式:
commit、rollback;

锁消除

删除改行记录(释放锁之后),其中一个事务将成功获取锁,从而执行成功,其他事务因死锁检测而回滚。

扩展:MySQL官方声明-不同语句产生的锁

SELECT … FROMis a consistent read,无锁
SELECT … FOR UPDATEandSELECT … FOR SHAREstatements:对于唯一索引,只对需要锁定的记录上record lock,如果不是唯一索引就会加Next-key lock,同时会对不满足条件的第一个record的前面加上gap lock
update和delete都2一样的策略
insert比较不一样,需要重点说下
首先,insert会对要插入的gap加insert intention gap lock,insert intention gap lock不像gap lock一样防止insert,insert intention gap lock互相之间可以共存,这样允许多个insert并发插入不同的位置

其次,对于要插入的位置会加入排他锁

多insert并发(至少3个),当插入的位置一样的时候(主键位置相同的时候),比如3个session都insert table id=x,其中第一个获得排他锁(行锁,写锁),其他两个session会产生duplicate-key error(后来的出现了重复的主键id),当duplicate-key error发生时,两个session都会将锁变化为共享锁,下一步获取排他锁(共享锁无法写入),然后第一个session rollback了,两个session互相持有共享锁,无法获得排他锁(两个一样行共享锁所以无法获取排他锁),导致死锁。

s1 insert id =x 或者 s1 delete id = x

s2 insert id = x

s3 insert id = x

s1 rollback

replace和insert一样

INSERT … ON DUPLICATE KEY UPDATE与insert不一样,当遇到 duplicate-key时,对于primary key获取排他记录锁,对于unique index获取Next-key lock,不会死锁

优化性能和死锁问题的思路:

尽量将热点行的操作延后
控制并发度,加队列

官方文档 不同语句产生死锁

posted @ 2022-10-28 23:29  在贝加尔湖畔  阅读(1671)  评论(0编辑  收藏  举报