MySQL中加载数据语句导致死锁的解决方案
1。背景
这是一个项目类似于数据分析和数据完全导入InnoDB表通过加载数据表。为了描述的方便,表结构简化如下:
创建测试表(ID为主键auto_increment,C诠释不为空);
导入数据的语句与
LOAD DATA INFILE ' DATA1 .csv进入测试表;
LOAD DATA INFILE '数据CSV到测试表;
猫data1.csv
一千一百
二千一百
三千一百
猫data2.csv
一万零一百
一万一千一百
一万二千一百
死锁的证明死锁信息出现在最新检测到的死锁段SHOW ENGINE INNODB STATUS,这是简化如下:
解释
正如你可以看到从上面的表中,交易1等线路上的锁,事务2持有行锁,但是等待表的自增锁(auto_inc)被认为是一个僵局和事务回滚。
这里没有写事务1,但是可以推断事务1持有表的自锁(否则它不是死锁)。
2。背景知识1:auto_inc锁及其选择
在InnoDB表,如果有一个自增字段、表级锁,称为自增锁,是维持。每一次新的数据插入,或UPDATE语句修改的字段,你需要获得这个锁
因为交易可能包含多个报表,并不是所有的陈述都自增字段相关的,所以会有专门的处理。自完成的锁在语句结束后立即释放,特殊处理的原因是普通锁在事务结束后全部释放。
如果一个表有一个自增字段,插入语句没有指定字段的值,或者指定为null,InnoDB将它分配到当前auto_increment值加1,然后auto_increment。
与此相关的一个参数是自加锁,innodb_autoinc_lock_mode默认值。1、和分别是可选的。
让我们看看当值设置为0,以及插入一行数据时,具有自增字段的表的行为:
1)申请auto_inc锁
2)获取当前auto_incremnt值N,加上1 auto_increment
3)执行插入操作,并将n填充到新行对应字段中。
4)释放auto_inc锁
我们看到,在这个过程中,虽然会降低锁的粒度,它释放出的句子后立即执行,但锁仍然太大--包括插入操作的时间。这导致了两个INSERT语句实际上不能平行。
在此参数之前,行为与设置为0相同,0选项兼容。
很容易想到设置1,应该是3)和4,但是,本文仍将讨论0的情况,因为我们的前提是负载语句,负载语句被插入到多行语句(包括插入…选择),即使设置为1,它也会退化为0。
三.后台知识的主从行为2:负载数据语句
你为什么要插入多行语句设置innodb_autoinc_lock_mode 1和使用0模式
主要的原因是因为主从一致。想象binlog_format =表,载入数据声明,直接记录在主图书馆的binlog声明本身,这是重播从图书馆:
1)将加载数据所使用的文件发送给奴隶,并从中保存临时目录中的文件。
2)在从属端执行负载数据语句。
同时,还有一个问题:奴隶如何保证负载数据语句的自增id字段与主控字段相同
为了解决这个问题,也有一套insert_id命令在主图书馆的binlog,这表明自增ID值的负荷数据表插入的第一行。这样从执行设置insert_id语句的集合执行负荷数据,确保执行的结果是作为主要的图书馆同之前。
上述机制可以保证主从数据的一致性:主从库上的负载数据语句生成的自增id值必须是连续的。
4, 1 + 2:背景知识分析
回到0和1之间的区别,我们可以看到,如果auto_inc锁在全句开始收购,它将在声明后发布,从而确保身份连续模式0通过整个句子生成的保证。
对于1,每次都释放下一个值。插入数据后,如果需要再次应用,则它是不连续的。
这就是为什么,即使设置为1,多线程操作,它将退化为0。
到目前为止,我们知道,这个僵局的原因是这两种负荷数据报表不仅可以访问相同的记录,但同时访问同一auto_inc锁,造成对方等待。
这不是结束,因为我们知道,虽然两个线程访问两锁会导致死锁,死锁的另一个条件,即应用顺序有关。因为auto_inc是表锁,无论谁得第一,那块在同一表中的其他数据加载的执行,和为什么会有锁等待一个记录
5。对背景知识3锁定时间:auto_inc
在我们的面前,我们说,每一次新的数据插入的auto_inc将会被锁定的过程是上市。但这个过程是一个设置列值为自我增值的InnoDB的情况。另一种情况是,该列的值一直在声明中指定的。
例如,将插入实现到TB值中,(9100)。id值已经是9,虽然不需要补到这个值,但在有可能需要改变auto_increment值(如果原来是小于10,则应改为10),所以锁或不。过程变成:
1)插入数据
2)如果失败是过程的结束
3)如果成功申请auto_inc锁
4)电话set_max…功能,如有必要,修改auto_increment
5)在语句结束auto_inc锁释放。
6。为什么要修改auto_inc秩序
这种调整的好处是什么主要目的是为了减少不必要的锁的访问。如果错误发生在插入数据,例如,其他领域造成重复键错误,所以你不必访问auto_inc锁。
7。死锁恢复过程
必须强调的是,在语句的结尾处,我们要查看一个加载数据语句的流,它被指定为每行的附加值(这是本文中的例子)。
1)插入第一个数据
2)申请auto_inc锁
3)插入第二部分
4)申请auto_inc锁(因为已经是自己的,直接成功)
5)插入所有剩余行。
6)释放auto_inc锁。
所以这个过程是简单的描述为:插入第一行,申请auto_inc锁,然后将所有剩余的行,然后释放它。
正如我们前面提到的,插入第一个数据时可能需要访问的记录锁直到整个事务结束时才被释放。
基于以上背景知识,我们将了解死锁的过程。
可以看出,引发的更高的要求,特别是如果session2是使用记录锁,由锁最。需要说明的是,同一记录锁并不意味着主关键字的值必须是相同的因为InnoDB内部的记录表示。
8。解决方法1:删除不必要的auto_increment场
在这个业务中,因为所有数据都是通过加载数据输入的,并且自指定增量字段的值已被指定,因此不需要自动增量属性。
少一点,死亡不能被锁住。
9,解决方案2:强制模式1
为innodb_autoinc_lock_mode参数上可选的值是0, 1,和2。设置为1时,负载数据声明将退化为模式0。但如果设置为2,模式1将用于无论如何。
我们前面提到的,使用模式1能使自增ID值的负载产生的数据是不连续的,结果在主人与奴隶的矛盾时,binlog_format是1,所以2的前提下,设置binlog_format是行。
在binlog_format = 'row ',它是安全的设置innodb_autoinc_lock_mode 2。
如果允许的话,方案2比方案1轻一点,不需要修改数据和表结构。