MySQL死锁原因分析

行级锁有三种模式:

innodb 行级锁 record-level lock大致有三种:record lock, gap lock and Next-KeyLocks。

record lock  锁住某一行记录

gap lock     锁住某一段范围中的记录

next key lock 是前两者效果的叠加。

问题:

行级锁表现形式:next-key lock

错误码: 1213
Deadlock found when trying to get lock; try restarting transaction,重点在于:Deadlock FOUND WHEN trying TO get LOCK; 表示行级锁冲突

解决:

解决方案,可能该更新处于一个大表,而且,表中不断有数据插入,将会出现这样的问题,可以使用更新标识在对应的更新分组上,然后,进行多次更新数据,如果出现锁,报错,事务回滚,然后,再次进行数据更新,这个锁发生的几率不大,所以,可以使用该方法进行解决

参考博客:

innodb 行级锁 record-level lock大致有三种:record lock, gap lock and Next-KeyLocks。

record lock  锁住某一行记录

gap lock     锁住某一段范围中的记录

next key lock 是前两者效果的叠加。

下面是MYSQL官方文档中相关内容的链接

http://dev.mysql.com/doc/refman/5.1/en/innodb-record-level-locks.html

【实验环境】

session 1 20:39:29> show create table gap \G

*************************** 1. row ***************************

Table: gap

Create Table: CREATE TABLE `gap` (

`id` int(11) DEFAULT NULL,

KEY `ind_gap_id` (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

session 1 20:39:32> select * from gap;

+------+

| id   |

+------+

|   17 |

|   20 |

|   33 |

|   39 |

|   42 |

|   43 |

+------+

6 rows in set (0.00 sec)

【实验】

两个会话都在REPEATABLE-READ 事务隔离级别。且都要在事务中进行。

session 1  20:39:37> start transaction;

Query OK, 0 rows affected (0.00 sec)

session 1  20:39:41> delete from gap where id=33;

Query OK, 1 row affected (0.00 sec)

session 20:40:07>

在会话2中 插入id <20 和 >=39的值 可以执行成功,而当要插入的id [20,39)的值时 会遇到gap lock 。

session 2 20:40:15> start transaction;

Query OK, 0 rows affected (0.00 sec)

session 2 20:40:30> insert into gap values(14);

Query OK, 1 row affected (0.00 sec)

session 2 20:40:59> insert into gap values(18);

Query OK, 1 row affected (0.00 sec)

session 2 20:41:06> insert into gap values(20);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

session 2 20:41:12> insert into gap values(24);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

session 2 20:42:17>

session 2 20:42:53> insert into gap values(35);

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

session 2 20:44:09>

session 2 20:44:56> insert into gap values(39);

Query OK, 1 row affected (0.00 sec)

session 2 20:45:13> insert into gap values(40);

Query OK, 1 row affected (0.00 sec)

从上面的实验中可以看出会话1 执行删除语句之后,不仅仅锁住 id=33的记录,同时也锁住区间为[20,39)的记录。具体的原因是执行delete from gap where id=33语句,mysql 会执行索引扫描并在该表上施加一个next-key lock ,向左扫描到20,向右扫描到39 ,锁定区间左闭右开,所以lock的范围是 [20,39)。

【gap 锁带来的问题】

生产环境中有这样的一个情况:

程序会对一个表message 进行update 和insert

session 1

UPDATE message SET gmt_modified = now(),deal_times = deal_times +1   , status = ‘sending‘ , gmt_retry = ‘2012-11-17 23:54:10‘

WHERE message_id=18;

insert into  message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)

values (‘hello !‘,-1,‘sending‘,‘instance_status_sync‘,2,127,now(),now(),now());

session 2

UPDATE message SET gmt_modified = now(),deal_times = deal_times +1   , status = ‘sending‘ , gmt_retry = ‘2012-11-17 23:54:10‘

WHERE message_id=19;

insert into  message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)

values          (‘hello world!‘,-2,‘sending‘,‘instance_status_sync‘,1,17,now(),now(),now());

对于上述程序在无并发情况下,运行正常,但是并发量大的情况下,执行顺序可能就会变成下面的:

UPDATE message SET gmt_modified = now(),deal_times = deal_times +1   , status = ‘sending‘ , gmt_retry = ‘2012-11-17 23:54:10‘

WHERE message_id= 61;

UPDATE message SET gmt_modified = now(),deal_times = deal_times +1   , status = ‘sending‘ , gmt_retry = ‘2012-11-17 23:54:10‘

WHERE message_id= 73;

insert into  message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)

values          (‘hello world!‘,-2,‘sending‘,‘instance_status_sync‘,1,17,now(),now(),now());

insert into  message (body ,user_id,status,message_type,version,deal_times,gmt_create,gmt_modified,gmt_retry)

values (‘hello !‘,-1,‘sending‘,‘instance_status_sync‘,2,127,now(),now(),now());

此时 往往会报错

[ERROR]  Could not execute Write_rows event on table db.message; Deadlock found when trying toget lock; ; try restarting transaction, Error_code: 1213;

前两条update 类型的语句都已经获得了[59,75 )区间内记录的S锁,然后两个事务又分别对该区间段内的message_id=10这个位置请求X锁,这时就发生死锁,谁都请求不到X锁,因为互相都持有S锁。

【解决方案有两种】

1、改变程序中数据库操作的逻辑

2、取消gap lock机制

Gap locking can be disabled explicitly.This occurs if you change the transaction isolation level to READ COMMITTED orenable the innodb_locks_unsafe_for_binlog system variable.

【参考】

http://dev.mysql.com/doc/refman/5.1/en/innodb-record-level-locks.html

http://dev.mysql.com/doc/refman/5.1/en/innodb-locks-set.html

博客地址:http://blog.itpub.net/22664653/viewspace-750824/

时间: 2025-01-06 09:23:00

MySQL死锁原因分析的相关文章

mysql 死锁简单分析

mysql都有什么锁 MySQL有三种锁的级别:页级.表级.行级,内存级(latch). 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高,并发度最低. 行级锁:开销大,加锁慢:会出现死锁:锁定粒度最小,发生锁冲突的概率最低,并发度也最高. 页面锁:开销和加锁时间界于表锁和行锁之间:会出现死锁:锁定粒度界于表锁和行锁之间,并发度一般 算法: next KeyLocks锁,同时锁住记录(数据),并且锁住记录前面的Gap Gap锁,不锁记录,仅仅记录前面的Gap Recordl

MySQL死锁案例分析与解决方案

现象: 数据库查询: SQL语句分析: mysql. 并发delete同一行记录,偶发死锁. delete from x_table where id=? 死锁分析: mysql的事务支持与存储引擎有关,MyISAM不支持事务,INNODB支持事务,更新时采用的是行级锁.这里采用的是INNODB做存储引擎,意味着会将update语句做为一个事务来处理.前面提到行级锁必须建立在索引的基础,这条更新语句用到了索引idx_1,所以这里肯定会加上行级锁. 行级锁并不是直接锁记录,而是锁索引,如果一条SQ

MySQL 死锁日志分析

------------------------ LATEST DETECTED DEADLOCK ------------------------ 140824  1:01:24 *** (1) TRANSACTION: TRANSACTION 110E, ACTIVE 73 sec starting index read   ## 事务ID=110E,活跃了73s mysql tables in use 1, locked 1 LOCK WAIT 3 lock struct(s), heap

Mysql死锁如何排查:insert on duplicate死锁一次排查分析过程

前言 遇到Mysql死锁问题,我们应该怎么排查分析呢?之前线上出现一个insert on duplicate死锁问题,本文将基于这个死锁问题,分享排查分析过程,希望对大家有帮助. 死锁案发还原 表结构: CREATE TABLE `song_rank` ( `id` int(11) NOT NULL AUTO_INCREMENT, `songId` int(11) NOT NULL, `weight` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`

MySQL 死锁与日志二三事

最近线上 MySQL 接连发生了几起数据异常,都是在凌晨爆发,由于业务场景属于典型的数据仓库型应用,白天压力较小无法复现.甚至有些异常还比较诡异,最后 root cause 分析颇费周折.那实际业务当中咱们如何能快速的定位线上 MySQL 问题,修复异常呢?下文我会根据两个实际 case,分享下相关的经验与方法. 1.Case1:部分数据更新失败 某天渠道同学反馈某报表极个别渠道数据为 0,大部分渠道数据正常.这个数据是由一个统计程序每天凌晨例行更新的,按理来说,要么全部正常,要么全部失败,那会

【Todo】Mysql死锁问题解决方式 &amp; 隔离级别等知识

参考了这篇文章:http://www.cnblogs.com/LBSer/p/5183300.html  <mysql死锁问题分析> 写的不错.

&lt;转&gt;一个最不可思议的MySQL死锁分析

1 死锁问题背景 1 1.1 一个不可思议的死锁 1 1.1.1 初步分析 3 1.2 如何阅读死锁日志 3 2 死锁原因深入剖析 4 2.1 Delete操作的加锁逻辑 4 2.2 死锁预防策略 5 2.3 剖析死锁的成因 6 3 总结 7 死锁问题背景 做MySQL代码的深入分析也有些年头了,再加上自己10年左右的数据库内核研发经验,自认为对于MySQL/InnoDB的加锁实现了如指掌,正因如此,前段时间,还专门写了一篇洋洋洒洒的文章,专门分析MySQL的加锁实现细节:<MySQL加锁处理分

一个最不可思议的MySQL死锁分析

一个最不可思议的MySQL死锁分析 死锁问题背景 做MySQL代码的深入分析也有些年头了,再加上自己10年左右的数据库内核研发经验,自认为对于MySQL/InnoDB的加锁实现了如指掌,正因如此,前段时间,还专门写了一篇洋洋洒洒的文章,专门分析MySQL的加锁实现细节:<MySQL加锁处理分析>. 但是,昨天"润洁"同学在<MySQL加锁处理分析>这篇博文下咨询的一个MySQL的死锁场景,还是彻底把我给难住了.此死锁,完全违背了本人原有的锁知识体系,让我百思不得

MySQL死锁问题实例分析及解决方法

MySQL死锁问题的相关知识是本文我们主要要介绍的内容,接下来我们就来一一介绍这部分内容,希望能够对您有所帮助. 1.MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表级锁,默认为页面锁 InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁 2.各种锁特点 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低 行级锁