一个线上项目报的死锁,简要说明一下产生原因、处理方案和相关的一些点.
1、背景
这是一个类似数据分析的项目,数据完全通过LOAD DATA语句导入一个InnoDB表中。为方便描述,表结构简化为如下:
Create table tb(id int primary key auto_increment, c int not null) engine=innodb;
导入数据的语句对应为
Load data infile ‘data1.csv' into table tb; Load data infile ‘data2.csv' into table tb;
cat Data1.csv 1 100 2 100 3 100 Cat data2.csv 10 100 11 100 12 100
产生死锁的证据是在show engine innodb status的LATEST DETECTED DEADLOCK段中看到死锁信息,简化为如下:
说明
从上面表格中看出,事务1在等待某一行的锁。而事务2持有这行的锁,但等待表的自增锁(AUTO_INC),判断为死锁,事务回滚。
这里事务1没有写出来,但是可以推断,事务1持有这个表的自增锁(否则就不是死锁了)。
2、背景知识1:AUTO_INC lock 及其选项
在InnoDB表中,若存在自增字段,则会维护一个表级别的锁,这里称为自增锁。每次插入新数据,或者update语句修改了此字段,都会需要获取这个锁
由于一个事务可能包含多个语句,而并非所有的语句都与自增字段有关,因此InnoDB作了一个特殊的处理,自增锁在一个语句结束后马上被释放。之所以说是特殊处理,是因为普通的锁,都是在事务结束后释放。
若一个表有自增字段,一个insert语句不指定该字段的值,或指定为NULL时,InnoDB会给它赋值为当前的AUTO_INCREMENT的值,然后AUTO_INCREMENT加1。
与这个自增锁相关的一个参数是innodb_autoinc_lock_mode. 默认值为1,可选为0,1,2。
我们先来看当这个值设置为0时,一个有自增字段的表,插入一行数据时的行为:
1) 申请AUTO_INC锁
2) 得到当前AUTO_INCREMNT值n,给AUTO_INCREMENT 加1
3) 执行插入操作,并将n填入新增的行对应字段中
4) 释放AUTO_INC锁
我们看到这个过程中,虽然InnoDB为了减少锁粒度,在语句执行完成就马上释放,但这锁还是太大了――它包括了插入操作的时间。这就导致了两个insert语句,实际上没办法并行。
没有这个参数之前,行为就是与设置为0相同,0这个选项就是留着兼容的。
很容易想到设置为1的时候,应该是将3) 和 4)对调。但是本文还是要讨论为0的情况,因为我们的前提是LOAD语句,而LOAD语句这类插入多行的语句中(包括insert …select …),即使设置为1也没用,会退化为0的模式。
3、背景知识2:LOAD DATA语句的主从行为
为什么插入多行的语句要即使将innodb_autoinc_lock_mode设置为1,也会用0的模式呢?
主要原因还是为了主从一致性。设想binlog_format='statement',一个LOAD DATA语句在主库的binlog直接记录为语句本身,那从库如何重放:
1) 将load data用到的文件发给slave,slave将文件保存在临时目录。
2) 在slave也执行一次LOAD DATA语句。
其间有一个问题:slave怎么保证load data语句的自增id字段与master相同?
为了解决这个问题,主库的binlog中还有一个set SET INSERT_ID命令,表明这个LOAD DATA语句插入的第一行的自增ID值。这样slave在执行load data之前,先执行了这个set SET INSERT_ID语句,用于保证执行结果与主库一模一样。
上述的机制能保证主从数据一致的前提是:主从库上LOAD DATA语句生成的自增ID值必须是连续的。
4、背景知识1+2:分析
回到前面说的模式0和1的区别,我们看到,如果AUTO_INC锁在整个语句开始之前就获取,在语句结束之后才释放,这样就能保证整个语句生成的id连续――模式0的保证。
对于1,每次拿到下一个值就释放,插入数据后,若需要再申请,则不连续。
这就是为什么,即使设置为1,对于多行操作,会退化成0。
至此我们知道这个死锁出现的原因,是这两个LOAD DATA语句不仅会访问相同的记录,还会访问同一个AUTO_INC锁,造成互相等待。
到此没完,因为我们知道虽然两个线程访问两个锁可能造成死锁,但是死锁还有另外一个条件,与申请顺序有关。既然AUTO_INC是一个表锁,不论谁先拿到,会阻塞其他同表的LOAD DATA的执行,又为什么会在某个记录上出现锁等待?
5、背景知识3:AUTO_INC的加锁时机
前面我们说到每次涉及到插入新数据,就会要求对AUTO_INC加锁,并列出了流程。但这个流程是对于需要从InnoDB中得到自增值来设置列值的情况。另一种情况是在语句中已经指定了该列的值。
比如对于这个表,执行 insert into tb values(9,100). 此时id的值已经明确是9,虽然不需要取值来填,但是插入这行后有可能需要改变AUTO_INCREMENT的值(若原来是
1) 插入数据
2) 若失败则流程结束
3) 若成功,申请AUTO_INC锁
4) 调用set_max….函数,如有必要则修改AUTO_INCREMENT
5) 语句结束时释放AUTO_INC锁。
6、为什么修改AUTO_INC顺序
这么调整的好处是什么? 主要是为了减少不必要的锁访问。若在插入数据期间发生错误,比如其他字段造成DUPLICATE KEY error,这样就不用访问AUTO_INC锁。
7、死锁过程复现
必须强调是“语句结束时”。这样我们来看一个每行都已经指定了自增列值的LOAD DATA语句的流程(也就是本文例子的情况):
1) 插入第一条数据
2) 申请AUTO_INC锁
3) 插入第二条
4) 申请AUTO_INC 锁(因为已经是自己的,直接成功)
5) 。。。。。。插入剩余所有行
6) 释放AUTO_INC锁。
所以这个流程就简单描述为:插入第一行,申请AUTO_INC锁,然后插入剩下的所有行后再释放。
我们前面提到过,插入第一条数据时可能需要访问的记录锁,是要等到整个事务结束后才释放的.
有了上面的这些背景知识,我们来复现一下死锁出现的过程
可以看到触发条件还是比较苛刻的,尤其是session2要刚好要用到session1锁住的那个记录锁。需要说明,由于InnoDB内部对记录的表示,同一个记录锁并不表示主键值一定相同。
8、解决方案1:去掉不必要的AUTO_INCREMENT字段
在这个业务中,由于所有的数据都是通过LOAD DATA进去,而且都已经指定了自增字段的值,因此这个AUTO)INCREMENT属性是不需要的。
少了一个,就死锁不了了。
9、解决方案2:强制模式1
前面我们说到innodb_autoinc_lock_mode这个参数的可选值有0、1、2。当设置为1的时候,在LOAD DATA语句会退化为模式0。但若设置为2,则无论如何都会使用模式1。
我们前面说到使用模式1会导致LOAD DATA生成的自增id值不连续,这样会导致在binlog_format是1时主从不一致,因此设置为2的前提,是binlog_format 是row.
在binlog_format='row'时,设置innodb_autoinc_lock_mode为2是安全的。
若允许,方案2比方案1更轻量些,不需要修改数据和表结构。
以上就是MySQL中由load data语句引起死锁的解决案例_MySQL的内容,更多相关内容请关注PHP中文网(www.php.cn)!

InnoDBBufferPool通过缓存数据和索引页来减少磁盘I/O,提升数据库性能。其工作原理包括:1.数据读取:从BufferPool中读取数据;2.数据写入:修改数据后写入BufferPool并定期刷新到磁盘;3.缓存管理:使用LRU算法管理缓存页;4.预读机制:提前加载相邻数据页。通过调整BufferPool大小和使用多个实例,可以优化数据库性能。

MySQL与其他编程语言相比,主要用于存储和管理数据,而其他语言如Python、Java、C 则用于逻辑处理和应用开发。 MySQL以其高性能、可扩展性和跨平台支持着称,适合数据管理需求,而其他语言在各自领域如数据分析、企业应用和系统编程中各有优势。

MySQL值得学习,因为它是强大的开源数据库管理系统,适用于数据存储、管理和分析。1)MySQL是关系型数据库,使用SQL操作数据,适合结构化数据管理。2)SQL语言是与MySQL交互的关键,支持CRUD操作。3)MySQL的工作原理包括客户端/服务器架构、存储引擎和查询优化器。4)基本用法包括创建数据库和表,高级用法涉及使用JOIN连接表。5)常见错误包括语法错误和权限问题,调试技巧包括检查语法和使用EXPLAIN命令。6)性能优化涉及使用索引、优化SQL语句和定期维护数据库。

MySQL适合初学者学习数据库技能。1.安装MySQL服务器和客户端工具。2.理解基本SQL查询,如SELECT。3.掌握数据操作:创建表、插入、更新、删除数据。4.学习高级技巧:子查询和窗口函数。5.调试和优化:检查语法、使用索引、避免SELECT*,并使用LIMIT。

MySQL通过表结构和SQL查询高效管理结构化数据,并通过外键实现表间关系。1.创建表时定义数据格式和类型。2.使用外键建立表间关系。3.通过索引和查询优化提高性能。4.定期备份和监控数据库确保数据安全和性能优化。

MySQL是一个开源的关系型数据库管理系统,广泛应用于Web开发。它的关键特性包括:1.支持多种存储引擎,如InnoDB和MyISAM,适用于不同场景;2.提供主从复制功能,利于负载均衡和数据备份;3.通过查询优化和索引使用提高查询效率。

SQL用于与MySQL数据库交互,实现数据的增、删、改、查及数据库设计。1)SQL通过SELECT、INSERT、UPDATE、DELETE语句进行数据操作;2)使用CREATE、ALTER、DROP语句进行数据库设计和管理;3)复杂查询和数据分析通过SQL实现,提升业务决策效率。

MySQL的基本操作包括创建数据库、表格,及使用SQL进行数据的CRUD操作。1.创建数据库:CREATEDATABASEmy_first_db;2.创建表格:CREATETABLEbooks(idINTAUTO_INCREMENTPRIMARYKEY,titleVARCHAR(100)NOTNULL,authorVARCHAR(100)NOTNULL,published_yearINT);3.插入数据:INSERTINTObooks(title,author,published_year)VA


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

Dreamweaver Mac版
视觉化网页开发工具

WebStorm Mac版
好用的JavaScript开发工具

禅工作室 13.0.1
功能强大的PHP集成开发环境