MySQL产生死锁原因分析讲解

目录
  • 锁类型介绍
  • 死锁产生原因和示例
    • 1、产生原因
    • 2、产生示例
      • 案例一
      • 案例二
      • 案例三
      • 案例四
      • 案例五
      • 案例六

锁类型介绍

MySQL 有三种锁的级别:页级、表级、行级

1 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高, 并发度最低。

2 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低, 并发度也最高。

3 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

死锁产生原因和示例

1、产生原因

所谓死锁 <DeadLock>:是指两个或两个以上的进程在执行过程中, 因争夺资源而造成的一种互相等待的现象, 若无外力作用,它们都将无法推进下去. 此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。表级锁不会产生死锁. 所以解决死锁主要还是针对于最常用的 InnoDB。

死锁的关键在于:两个 (或以上) 的 Session 加锁的顺序不一致。

那么对应的解决死锁问题的关键就是:让不同的 session 加锁有次序。

2、产生示例

案例一

需求:将投资的钱拆成几份随机分配给借款人。

起初业务程序思路是这样的:

投资人投资后,将金额随机分为几份,然后随机从借款人表里面选几个,然后通过一条条 select for update 去更新借款人表里面的余额等。

例如两个用户同时投资:

A 用户金额随机分为 2 份,分给借款人 1,2

B 用户金额随机分为 2 份,分给借款人 2,1

由于加锁的顺序不一样,死锁当然很快就出现了。

对于这个问题的改进很简单,直接把所有分配到的借款人直接一次锁住就行了。

Select * from xxx where id in (xx,xx,xx) for update

在 in 里面的列表值 mysql 是会自动从小到大排序,加锁也是一条条从小到大加的锁例如(以下会话 id 为主键):

Session1:

mysql> select * from t3 where id in (8,9) for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
|  8 | WA     | f    | 2016-03-02 11:36:30 |
|  9 | JX     | f    | 2016-03-01 11:36:30 |
+----+--------+------+---------------------+
rows in set (0.04 sec)

Session2:

select * from t3 where id in (10,8,5) for update;

锁等待中……

其实这个时候 id=10 这条记录没有被锁住的,但 id=5 的记录已经被锁住了,锁的等待在 id=8 的这里 不信请看。

Session3:

mysql> select * from t3 where id=5 for update;

锁等待中

Session4:

mysql> select * from t3 where id=10 for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
| 10 | JB     | g    | 2016-03-10 11:45:05 |
+----+--------+------+---------------------+
row in set (0.00 sec)

在其它 session 中 id=5 是加不了锁的,但是 id=10 是可以加上锁的。

案例二

在开发中,经常会做这类的判断需求:根据字段值查询(有索引),如果不存在,则插入;否则更新。

以 id 为主键为例,目前还没有 id=22 的行

Session1:

select * from t3 where id=22 for update;
Empty set (0.00 sec)

session2:

select * from t3 where id=23  for update;
Empty set (0.00 sec)

Session1:

insert into t3 values(22,'ac','a',now());

锁等待中……

Session2:

insert into t3 values(23,'bc','b',now());

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

当对存在的行进行锁的时候 (主键),mysql 就只有行锁。

当对未存在的行进行锁的时候 (即使条件为主键),mysql 是会锁住一段范围(有 gap 锁)

锁住的范围为:

(无穷小或小于表中锁住 id 的最大值,无穷大或大于表中锁住 id 的最小值)

如:如果表中目前有已有的 id 为(11 , 12),那么就锁住(12,无穷大)

如果表中目前已有的 id 为(11 , 30),那么就锁住(11,30)

对于这种死锁的解决办法是:

insert into t3(xx,xx) on duplicate key update `xx`='XX';

用 mysql 特有的语法来解决此问题。

因为 insert 语句对于主键来说,插入的行不管有没有存在,都会只有行锁。

案例三

mysql> select * from t3 where id=9 for update;
+----+--------+------+---------------------+
| id | course | name | ctime               |
+----+--------+------+---------------------+
|  9 | JX     | f    | 2016-03-01 11:36:30 |
+----+--------+------+---------------------+
row in set (0.00 sec)

Session2:

mysql> select * from t3 where id<20 for update;

锁等待中…

Session1:

mysql> insert into t3 values(7,'ae','a',now());

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

这个跟案例一其它是差不多的情况,只是 session1 不按常理出牌了。

Session2 在等待 Session1 的 id=9 的锁,session2 又持了 1 到 8 的锁(注意 9 到 19 的范围并没有被 session2 锁住),最后,session1 在插入新行时又得等待 session2, 故死锁发生了。

这种一般是在业务需求中基本不会出现,因为你锁住了 id=9,却又想插入 id=7 的行,这就有点跳了,当然肯定也有解决的方法,那就是重理业务需求,避免这样的写法。

案例四

一般的情况,两个 session 分别通过一个 sql 持有一把锁,然后互相访问对方加锁的数据产生死锁。

案例五

两个单条的 sql 语句涉及到的加锁数据相同,但是加锁顺序不同,导致了死锁。

案例六

CREATE TABLE dltask (
    id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
    a varchar(30) NOT NULL COMMENT 'uniq.a',
    b varchar(30) NOT NULL COMMENT 'uniq.b',
    c varchar(30) NOT NULL COMMENT 'uniq.c',
    x varchar(30) NOT NULL COMMENT 'data',
    PRIMARY KEY (id),
    UNIQUE KEY uniq_a_b_c (a, b, c)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='deadlock test';

a,b,c 三列,组合成一个唯一索引,主键索引为 id 列。

事务隔离级别:RR (Repeatable Read)

每个事务只有一条 SQL:

delete from dltask where a=? and b=? and c=?;

到此这篇关于MySQL产生死锁原因分析讲解的文章就介绍到这了,更多相关MySQL死锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MySQL锁等待与死锁问题分析

    前言: 在 MySQL 运维过程中,锁等待和死锁问题是令各位 DBA 及开发同学非常头痛的事.出现此类问题会造成业务回滚.卡顿等故障,特别是业务繁忙的系统,出现死锁问题后影响会更严重.本篇文章我们一起来学习下什么是锁等待及死锁,出现此类问题又应该如何分析处理呢? 1.了解锁等待与死锁 出现锁等待或死锁的原因是访问数据库需要加锁,那你可能要问了,为啥要加锁呢?原因是为了确保并发更新场景下的数据正确性,保证数据库事务的隔离性. 试想一个场景,如果你要去图书馆借一本<高性能MySQL>,为了防止有人

  • MySQL死锁使用详解及检测和避免方法

    目录 前言 锁的释放与阻塞 死锁的发生和检测 查看锁信息(日志) 死锁的避免 前言 上一篇博客我们知道的Mysql事务的隔离机制和实现,以及锁的详细解析 链接: MySQL脏读幻读不可重复读及事务的隔离级别和MVCC.LBCC实现 在我们使用锁的时候,有一个问题是需要注意和避免的,我们知道,排它锁有互斥的特性.一个事务或者说一个线程持有锁的时候,会阻止其他的线程获取锁,这个时候会造成阻塞等待,如果循环等待,会有可能造成死锁. 这个问题我们需要从几个方面来分析,一个是锁为什么不释放,第二个是被阻塞

  • MySQL线上死锁分析实战

    前言 MySQL 的锁机制相信大家在学习 MySQL 的时候都有简单的了解过,那既然有锁就必定绕不开死锁这个问题.其实 MySQL 在大部分场景下是不会存在死锁问题的(比如并发量不高,SQL 写得不至于太拉胯的情况),但是在高并发的业务场景下,一不注意就会产生死锁,而这个死锁分析起来也比较麻烦. 前段时间在公司实习的时候就遇到了一个比较奇怪的死锁,之前一直没来得及好好整理,最近有空复现了一下,算是积累一点经验. 业务场景 简单说一下业务背景,公司做的是电商直播,我负责的是主播端相关的业务.而这个

  • 阿里面试MySQL死锁问题的处理

    目录 1.什么是死锁 2.InnoDB锁类型 2.1.间隙锁(gaplock) 2.2.next-keylock 2.3.意向锁(Intentionlock) 2.4.插入意向锁(InsertIntentionlock) 2.5.锁模式兼容矩阵 3.阅读死锁日志 3.1.日志分析如下: 4.经典案例分析 4.1.事务并发insert唯一键冲突 4.2.先update再insert的并发死锁问题 5.如何尽可能避免死锁 结尾 咱们使用 MySQL 大概率上都会遇到死锁问题,这实在是个令人非常头痛的

  • Mysql超详细讲解死锁问题的理解

    目录 1.什么是死锁? 2.Mysql出现死锁的必要条件 资源独占条件 请求和保持条件 不剥夺条件 相互获取锁条件 3. Mysql经典死锁案例 3.1 建表语句 3.2 初始化相关数据 3.3 正常转账过程 3.4 死锁转账过程 3.5 死锁导致的问题 4.如何解决死锁问题? 4.1 打破请求和保持条件 4.2 打破相互获取锁条件(推荐) 5.总结 1.什么是死锁? 死锁指的是在两个或两个以上不同的进程或线程中,由于存在共同资源的竞争或进程(或线程)间的通讯而导致各个线程间相互挂起等待,如果没

  • MySQL产生死锁原因分析讲解

    目录 锁类型介绍 死锁产生原因和示例 1.产生原因 2.产生示例 案例一 案例二 案例三 案例四 案例五 案例六 锁类型介绍 MySQL 有三种锁的级别:页级.表级.行级 1 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突的概率最高, 并发度最低. 2 行级锁:开销大,加锁慢:会出现死锁:锁定粒度最小,发生锁冲突的概率最低, 并发度也最高. 3 页面锁:开销和加锁时间界于表锁和行锁之间:会出现死锁:锁定粒度界于表锁和行锁之间,并发度一般. 死锁产生原因和示例 1.产生原因 所谓死锁

  • mysql 数据库死锁原因及解决办法

    死锁(Deadlock) 所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程.由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁. 一种情形,此时执行程序中两个或多个线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源.例如,如果线程A锁住了记

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

    MySQL死锁问题是很多程序员在项目开发中常遇到的问题,现就MySQL死锁及解决方法详解如下: 1.MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表级锁,默认为页面锁 InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁 2.各种锁特点 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低 行级锁:开销大,加锁慢;

  • MySQL数据库的一次死锁实例分析

    1.故事起因于2016年11月15日的一个生产bug.业务场景是:归档一个表里边的数据到历史表里边,同是删除主表记录. 2.背景场景简化如下(数据库引擎InnoDb,数据隔离级别RR[REPEATABLE]) -- 创建表test1 CREATE TABLE test1 ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(10) NOT NULL, PRIMARY KEY (id) ); insert into test1 values('hel

  • MySQL 出现错误1418 的原因分析及解决方法

    MySQL 出现错误1418 的原因分析及解决方法 具体错误: 使用mysql创建.调用存储过程,函数以及触发器的时候会有错误符号为1418错误. ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL,or READS SQL DATA in its declaration and binary logging is enabled(you *might* want to use the less safe log

  • MYSQL数据表损坏的原因分析和修复方法小结(推荐)

    1.表损坏的原因分析 以下原因是导致mysql 表毁坏的常见原因: 1. 服务器突然断电导致数据文件损坏. 2. 强制关机,没有先关闭mysql 服务. 3. mysqld 进程在写表时被杀掉. 4. 使用myisamchk 的同时,mysqld 也在操作表. 5. 磁盘故障. 6. 服务器死机. 7. mysql 本身的bug . 2.表损坏的症状 一个损坏的表的典型症状如下: 1 .当在从表中选择数据之时,你得到如下错误: Incorrect key file for table: '...

  • MySql范围查找时索引不生效问题的原因分析

    1 问题描述 本文对建立好的复合索引进行排序,并取记录中非索引字段,发现索引不生效,例如,有如下表,DDL语句为: CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` enum('M','F') NOT NULL, `hire_da

  • MySQL定时任务不能正常执行的原因分析及解决方法

    目录 前言 原因分析及解决方法 让定时任务快速执行 总结 前言 在使用数据库定时任务时,常常会出现定时任务不执行的问题,现对该问题出现的原因及解决方案做一些分析和整理. 原因分析及解决方法 当我们发现MySQL的定时任务没有执行时,首先去定时任务中查看[上次运行]时间是否正确,判断其是否正常执行,如果正常执行则需要考虑是否是事件逻辑不正确. 如果上次运行时间为空或者不正确,则需要查看数据库是否打开了定时任务,通过执行下列查询语句进行查询 show VARIABLES like '%event_s

  • MySQL 案例分析讲解外连接语法

    目录 前言 左连接 例 1 右连接 例2 作业记录 前言 外连接可以分为左外连接和右外连接 左外连接: 包含左边表的全部行(不管右边的表中是否存在与它们匹配的行),以及右边表中全部匹配的行 右外连接: 包含右边表的全部行(不管左边的表中是否存在与它们匹配的行),以及左边表中全部匹配的行 左连接 左外连接又称为左连接,使用 LEFT OUTER JOIN 关键字连接两个表,并使用 ON 子句来设置连接条件. 左连接的语法格式如下: SELECT <字段名> FROM <表1> LEF

随机推荐