mysql innodb 死锁

mysql innodb 死锁 2011-03-21 10:14:02

分类: Mysql/postgreSQL

innodb大量AUTO-INCLOCK 导致的DeadLOCK1.现象

今天发现数据库报检查到死锁, show innodb status \G但从STATUS看,都是 lock mode AUTO-INC waiting.而且从应用开发了解,都是简单的INSERT和select。 没有交叉更新的事务。 

如果是等待AUTO-INC锁,也不应该会产生死锁。因为AUTO-INC锁在某一时刻只会被一个事务/INSERT操作占有。INSERT一旦完成就会释放AUTO-INC锁。

那怎么会有死锁呢?

2.原因

今天查了一下资料,大致了解了当innodb_autoinc_lock_mode=0(mysql5.1.22之前连这个选项都没添加,所以默认都是0)时,在并发数大于208以上可能出现很多死锁的原因,如下:

 

在innodb源代码lock/lock0lock.c文件中,定义了两个常量:

 

/* Restricts thelength of search we will do in the waits-for

     graph of transactions */

#defineLOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK 1000000

 

/* Restricts therecursion depth of the search we will do in the waits-for

    graph of transactions */

#defineLOCK_MAX_DEPTH_IN_DEADLOCK_CHECK 200

 

然后在检查是否产生死锁的函数lock_deadlock_occurs()中有如下代码:

 

ret =lock_deadlock_recursive(trx, trx, lock, &cost, 0);

switch (ret) {

caseLOCK_EXCEED_MAX_DEPTH:

        产生死锁

        ...

        break;

}

其中的lock_deadlock_recursive()函数是递归函数,它会检查自身递归深度,其中有如下代码:

 

ibool   too_far

   = depth >LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK

||*cost > LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK;

...

if (too_far) {

                return(LOCK_EXCEED_MAX_DEPTH);

            }

因此innodb在检查是否产生死锁时调用lock_deadlock_occurs()检查,这个函数再会调用lock_deadlock_recursive()递归检查锁的数目(不知道这么说是否确切?),当递归的深度depth大于了一开始介绍的常量LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK,或者cost(不清楚这个代表什么)大于一开始介绍的常量LOCK_MAX_N_STEPS_IN_DEADLOCK_CHECK时,就认为发生了死锁.

当并发数超过208的时候,发生了很多死锁,基于上面的分析我们可以得知,因为每一个并发做的是一个insert操作,需要加一个锁(暂且不管是s锁还是x锁),200多个线程并发执行,第n个insert正在执行,后来的线程无法获得锁,于是加入到lockqueue中排队等待,这个时候lockqueue长度超过了常量LOCK_MAX_DEPTH_IN_DEADLOCK_CHECK所默认定义的200,然后Innodb在检查是否产生死锁的时候发现在递归检查lock_queue的时候深度超过了200,于是就认为发生了死锁.

废话了这么多就是当并发数超过208的时候mysql认为发生了死锁,因为AUTO-INC锁在mysql 5.1.22前,这个是表级锁,如果并发高、数据量大的话,很容易就到达208的

3.防止

找到原因后,下面就是怎么防止这个死锁的产生呢?

上面的测试也说了,在mysql 5.1.22中,已经改良了Innodb的auto_increment的锁机制,增加了一个innodb_autoinc_lock_mode选项,我们只需要使用其默认值1就可以很好的避免这个问题.

3.1 INSERT-like

在mysql5.1.22之前,mysql的“INSERT-like”语句(包INSERT,INSERT…SELECT, REPLACE,REPLACE…SELECT, and LOAD DATA)会在执行整个语句的过程中使用一个AUTO-INC锁将表锁住,直到整个语句结束(而不是事务结束)。因此在使用INSERT…SELECT、INSERT…values(…),values(…)时,LOADDATA等耗费时间较长的操作时,会将整个表锁住,而阻塞其他的“INSERT-like”、Update等语句,推荐使用程序将这些语句分成多条语句,一一插入,减少单一时间的锁表时间。

mysql5.1.22之后mysql进行了改进,引入了参数innodb_autoinc_lock_mode,通过这个参数控制mysql的锁表逻辑,在介绍这个之前先引入几个术语,方便说明innodb_autoinc_lock_mode。

INSERT-like:

INSERT, INSERT …SELECT, REPLACE, REPLACE … SELECT, and LOAD DATA, INSERT … VALUES(),VALUES()

 

Simpleinserts

就是通过分析insert语句可以确定插入数量的insert语句,INSERT, INSERT … VALUES(),VALUES()

 

Bulkinserts

就是通过分析insert语句不能确定插入数量的insert语句,INSERT … SELECT, REPLACE … SELECT, LOAD DATA

 

Mixed-modeinserts

下面两种,不确定是否需要分配auto_incrementid

INSERTINTO t1 (c1,c2) VALUES (1,’a'), (NULL,’b'), (5,’c'), (NULL,’d');

INSERT… ON DUPLICATE KEY UPDATE

3.2 innodb_autoinc_lock_mode选项3.2.1 innodb_autoinc_lock_mode = 0(“traditional” lock mode)

这种方式就和mysql5.1.22以前一样,为了向后兼容而保留了这种模式,如同前面介绍的一样,这种方式的特点就是“表级锁定”,并发性较差

3.2.2 innodb_autoinc_lock_mode = 1(“consecutive” lock mode)

这种方式是新版本中的默认方式,推荐使用,并发性相对较高,特点是“consecutive”,即保证同一条insert语句中新插入的auto_incrementid都是连续的。

 

这种模式下:

“Simple inserts”:直接通过分析语句,获得要插入的数量,然后一次性分配足够的auto_incrementid,只会将整个分配的过程锁住。

 

“Bulk inserts”:因为不能确定插入的数量,因此使用和以前的模式相同的表级锁定。

 

“Mixed-modeinserts”:直接分析语句,获得最坏情况下需要插入的数量,然后一次性分配足够的auto_incrementid,只会将整个分配的过程锁住。需要注意的是,这种方式下,会分配过多的id,而导致”浪费“。比如INSERTINTO t1 (c1,c2) VALUES (1,’a'), (NULL,’b'), (5,’c'), (NULL,’d');会一次性的分配5个id,而不管用户是否指定了部分id;INSERT … ONDUPLICATE KEY UPDATE一次性分配,而不管将来插入过程中是否会因为duplicatekey而仅仅执行update操作。

注意:当master mysql版本<5.1.22,slave mysql版本>=5.1.22时,slave需要将innodb_autoinc_lock_mode设置为0,因为默认的innodb_autoinc_lock_mode为1,对于INSERT… ON DUPLICATE KEY UPDATE和INSERTINTO t1 (c1,c2) VALUES (1,’a'), (NULL,’b'), (5,’c'), (NULL,’d');的执行结果不同,现实环境一般会使用INSERT… ON DUPLICATE KEY UPDATE。

3.2.3 innodb_autoinc_lock_mode = 2(“interleaved” lock mode)

这种模式是来一个分配一个,而不会锁表,只会锁住分配id的过程,和innodb_autoinc_lock_mode= 1的区别在于,不会预分配多个,这种方式并发性最高。但是在replication中当binlog_format为statement-based时(简称SBRstatement-based replication)存在问题,因为是来一个分配一个,这样当并发执行时,“Bulkinserts”在分配的时会同时向其他的INSERT分配,会出现主从不一致(从库执行结果和主库执行结果不一样),因为binlog只会记录开始的insertid。

 

测试SBR,执行begin;insertvalues(),();insert values(),();commit;会在binlog中每条insertvalues(),();前增加SETINSERT_ID=18/*!*/;。

 

但是row-basedreplication RBR时不会存在问题。

 

另外RBR的主要缺点是日志数量在包括语句中包含大量的updatedelete(update多条语句,delete多条语句)时,日志会比SBR大很多;假如实际语句中这样语句不是很多的时候(现实中存在很多这样的情况),推荐使用RBR配合innodb_autoinc_lock_mode,不过话说回来,现实生产中“Bulkinserts”本来就很少,因此innodb_autoinc_lock_mode= 1应该是够用了。
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随与我联系,我会及为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随与我联系,我会及解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值