近日遇到一个比较奇怪的deadlock错误, 错误详情:

Deadlock found when trying to get lock; try restarting transaction; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException...

跟踪代码后最终定位到一段业务逻辑:

delete from A where no = $no;
insert into A(no, value) values($no, "value");

印象中mysql一直是使用行级锁, 为什么此处在并发时会发生死锁呢? 唯一的解释是mysql在这边锁住的不只一行数据.

简单搜索之后, 发现mysql的锁分为三种(按照锁定的行数划分):
1.record lock:记录锁,也就是仅仅锁着单独的一行
2.gap lock:区间锁,仅仅锁住一个区间(注意这里的区间都是开区间,也就 是不包括边界值,至于为什么这么定义?innodb官方定义的)
3.next-key lock:record lock+gap lock,所以next-key lock也就半开半闭区间,且是下界开,上界闭。(为什么这么定义?innodb官方定义的)

由于此处是在明确指定了no=XX的情况下抛出了死锁异常, 并且no建立的是普通索引, 所以此处mysql使用的应该是next-key lock(查看何种情况下使用何种锁 https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html).

下面来举个手册上的例子看看next-key lock是如何上锁的。假如一个索引的行有10,11,13,20
那么可能的next-key lock的包括:
(无穷小, 10]
(10,11]
(11,13]
(13,20]
(20, 无穷大)

下面分析何种情况下会发生死锁.
结合业务逻辑, 执行新增操作时也会执行一样的逻辑, 先进行delete.
例如,现在表student中有四条数据:

Image 1

现在要新增一条数据, no = 21, 这时候会先进行delete, 线程会锁住(20, 无穷大)这块区间, 加入这个时候另一个线程正在新增另一条数据 no = 22, 线程也会锁住(20, 无穷大)这块区间就会发生死锁.

下面看具体实验:
创建一张表student.

CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`no` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_no` (`no`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=latin1

插入数据:

INSERT INTO student (no,name) VALUES(10, "Jim");
INSERT INTO student (no,name) VALUES(11, "Kimi");
INSERT INTO student (no,name) VALUES(13, "Tom");
INSERT INTO student (no,name) VALUES(20, "Mike");

Image 2

执行两个事务:
session 1:

begin;
delete from student where no = 21;

session 2:

begin;
delete from student where no = 22;

此处解释一下, 此时,session 1和session 2都会对区间(20, 无穷大)加锁, 而区间锁只是用来防止其他事务在区间中插入数据,区间x锁 与区间S锁效果是一样的(只要不是插入操作), 因此两个session都会持有锁.

参考:https://dev.mysql.com/doc/refman/5.1/en/innodb-record-level-locks.html
Gap locks in InnoDB are “purely inhibitive”, which means they only stop other transactions from inserting to the gap. Thus, a gap X-lock has the same effect as a gap S-lock.

继续执行:
session 1:

INSERT INTO student (no,name) VALUES(21, "Zhoubing");

此时session 1阻塞(因为session 2持有区间锁), 如图:

Image 3

session 2:

INSERT INTO student (no,name) VALUES(22, "Zhoubing");

此时session 2死锁(因为session 1持有区间锁), 如图:

Image 4

.

总结, delete之后进行insert有可能发生死锁, 因为delete可能会持有区间锁, 而区间锁是可重入的(只要不是插入数据).

解决方案:
将事务隔离级别将为read commit.

[转]一次Delete&Insert引发的Mysql死锁的更多相关文章

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

    前言 遇到Mysql死锁问题,我们应该怎么排查分析呢?之前线上出现一个insert on duplicate死锁问题,本文将基于这个死锁问题,分享排查分析过程,希望对大家有帮助. 死锁案发还原 表结构 ...

  2. delete数组引发的core分析

    delete [] ptr 引发了singnal 6 abort的core错误,跟踪过程发现写入ptr大量数据,引发内存越界,破坏了new数组的尾部数据保护,导致delete的时候core. 问题分析 ...

  3. [转]SQLServer添加UPDATE回滚日志(update/delete/insert)

    下面直接上代码(copy到你的数据库里面直接就可以运行): CREATE PROCEDURE [dbo].[SP_UPDATE_LOG] ) AS BEGIN SET NOCOUNT ON; IF N ...

  4. MySQL 死锁与日志二三事

    最近线上 MySQL 接连发生了几起数据异常,都是在凌晨爆发,由于业务场景属于典型的数据仓库型应用,白天压力较小无法复现.甚至有些异常还比较诡异,最后 root cause 分析颇费周折.那实际业务当 ...

  5. MySQL 死锁问题分析

    转载: MySQL 死锁问题分析 线上某服务时不时报出如下异常(大约一天二十多次):"Deadlock found when trying to get lock;". Oh, M ...

  6. Mysql 死锁问题

    Innodb锁系统(4) Insert/Delete 锁处理及死锁示例分析 http://mysqllover.com/?p=431 关于innodb死锁 http://afei2.sinaapp.c ...

  7. 【错误记录】flask mysql 死锁

    最近使用flask-sqlalchemy时,进行测试的时候发现日志中打印出了MySql死锁错误,查看Mysql日志发现是因为有俩条sql出现了死锁: Deadlock found when tryin ...

  8. MySQL死锁及解决方案

    一.MySQL锁类型 1. MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表 ...

  9. mysql死锁——mysql之四

    1.MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表级锁,默认为页面锁 In ...

随机推荐

  1. Tkinter PanedWindow

         Tkinter PanedWindow: 一个PanedWindow是一个容器部件可能包含任何数量的窗格,水平或垂直排列 一个PanedWindow是一个容器部件可能包含任何数量的窗格,水平 ...

  2. nginx作用

    图解nginx作用:

  3. NodeJs中require use get typescript及其他知识点集合

    NodeJs的Express使用 nodejs事件的监听与事件的触发 TypeScript学习笔记 深入浅出Node.js Nodejs开发Office插件 类百度文库文档上传.转换和展示功能项目开源 ...

  4. PHP中使用CURL模拟文件上传实例

    调用实例: 该方法将本地的E盘文件test.doc上传到接口服务器上的 uploadFile方法中,uploadFile会对上传的文件做进一步处理. 若你想自己对上传的文件做操作,将接口uploadF ...

  5. chrome浏览器控制台创建js脚本并执行

    Chrome的snippets是小脚本,还可以创作并在Chrome DevTools的来源面板中执行.您可以访问和从任何页面运行它们.当你运行一个片段,它从当前打开的页面的上下文中执行.本文主要讲如何 ...

  6. 详解C++右值引用

    C++0x标准出来很长时间了,引入了很多牛逼的特性[1].其中一个便是右值引用,Thomas Becker的文章[2]很全面的介绍了这个特性,读后有如醍醐灌顶,翻译在此以便深入理解. 目录 概述 mo ...

  7. 类似jQuery的原生JS封装的ajax方法

    一,前言: 前文,我们介绍了ajax的原理和核心内容,主要讲的是ajax从前端到后端的数据传递的整个过程. Ajax工作原理和原生JS的ajax封装 真正的核心就是这段代码: var xhr = ne ...

  8. 377. Combination Sum IV 返回符合目标和的组数

    [抄题]: Given an integer array with all positive numbers and no duplicates, find the number of possibl ...

  9. Redis学习(2)—— 实例与注释说明[转]

    import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import ...

  10. C# 实现对PPT编辑

    C# Presentation 文本替换 我们可以通过插入占位符的方式,使用新的字词替换已有幻灯片里的文字. 本文将详细描述如何使用Spire.Presentation 来替换Prsentation ...