MySQL innoDB 间隙锁产生的死锁问题

这篇具有很好参考价值的文章主要介绍了MySQL innoDB 间隙锁产生的死锁问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

线上经常偶发死锁问题,当时处理一张表,也没有联表处理,但是有两个mq入口,并且消息体存在一样的情况,频率还不是很低,这么一个背景,我非常容易怀疑到,两个消息同时近到这一个事务里面导致的,但是是偶发的,又模拟不出来什么场景会导致死锁,只能进行代码分析,问题还原的方式去排查问题。

业务代码简化成下面

begin


update test set yn = 0 where dm_code = "3";
SELECT  * from test where dm_code = '3'
INSERT INTO demand_flow_followers (dm_code, erp )
values
('3', 'a')
,
('3', 'b')
,
('3', 'c')





也就是说先update ,select , insert 这么一个顺序

表中存在dm_code ,erp 唯一索引
如果不存在索引 第一行update 会导致行锁升级为表锁,反而不会导致问题出现,但是并发太差

结论

先说结论:

session1 session2
开启事务
update
开启事务
update
insert
insert出现死锁

重点: 无论哪个事务insert,两个事务必须都update 完成,只要满足这个条件,两个insert执行的时候就会报死锁

原因:我先按照自己的理解解释下:

innodb的行锁,存在间隙锁,为啥要去有索引,如果没有索引,第一个update 就直接进行了表锁,这样导致另外一个事务无法进入,就只能进行等待了。

有索引的情况下:

两个事务都执行update,都拿到了[当前值,+∞) 的锁(记录锁+间隙锁),(update的时候,无数据命中)
第一个insert时,希望等待另外一个事务释放锁。第二个事务希望第一个事务释放锁,因此出现了死锁问题

相关知识梳理

InnoDB有三种行锁的算法:

1.Record Lock:是加在索引记录上的。

2.Gap Lock(间隙锁):对索引记录间的范围加锁,或者加在最后一个索引记录的前面或者后面

3.Next-Key Lock:前两种锁的结合,锁定一个范围,并且锁定记录本身,主要目的是解决幻读的问题。

间隙锁主要是防止幻象读,用在Repeated-Read(简称RR)隔离级别下。在Read-Commited(简称RC)下,一般没有间隙锁(有外键情况下例外,此处不考虑)。间隙锁还用于statement based replication

间隙锁有些副作用,如果要关闭,一是将会话隔离级别改到RC下,或者开启 innodb_locks_unsafe_for_binlog(默认是OFF)。

间隙锁(无论是S还是X)只会阻塞insert操作。

CREATE TABLE `test` (


  `id` bigint(20) NOT NULL,


  `k` bigint(20) DEFAULT '0',


  PRIMARY KEY (`id`),


  KEY `idx_k` (`k`)


) ENGINE=InnoDB DEFAULT CHARSET=utf8
INSERT into test values(2,2),(5,5),(10,10)



select @@global.tx_isolation, @@tx_isolation;



RR隔离级别

delete from test where k=5;



session2

insert into test (id,k) values (3,3)
insert into test (id,k) values (4,4)
insert into test (id,k) values (6,6)
insert into test (id,k) values (7,7)
insert into test (id,k) values (8,8)
insert into test (id,k) values (9,9)



上面都报错:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
这个证明id (3,5)都被间隙锁锁住了

insert into test (id,k) values (1,1)
insert into test (id,k) values (11,11)
delete from test where id in (1,11)



(3,5) 区间之外都可以执行insert,delete操作

可以看到,delete k=5的记录阻塞了k=3、4、5、6、7、8、9记录的插入操作,事实上,除了对于k=5这条记录上record lock之外,innoDB对于delete和update在辅助索引(非主键索引)上的条件时会对扫过的记录上间隙锁,为了防止幻读,会锁住k=5这条记录的前面一条记录(id=2,k=2)到后面一条记录(id=10,k=10)之间的区间,即锁住k在区间(2,10)的范围(如果没有后一条记录,一直锁到正无穷),至于在边界k=2及k=10上,由于索引内是按照主键排序的,不会锁住(id<2,k=2)但是会锁住(id>2,k=2),同理不会锁住(id>10,k=10)但是会锁住(id<10,k=10).

insert into test (id,k) values (1,2) ok
insert into test (id,k) values (11,2) no
insert into test (id,k) values (11,9) no
insert into test (id,k) values (11,10) ok
insert into test (id,k) values (1,10) no
insert into test (id,k) values (11,10) ok



由于索引内是按照主键排序的,不会锁住(id<2,k=2)但是会锁住(id>2,k=2),同理不会锁住(id>10,k=10)但是会锁住(id<10,k=10).

值得注意的是,delete和update在唯一索引(primary key/unique key)上更新存在的记录时只会上行级记录锁(record key),而在唯一索引上更新不存在的记录时同辅助索引一样会上间隙锁;在上例中,delete id=5只会在(id=5,k=5)这条记录上上X锁,而delete id=7却会锁住(id>5&&id<10)这个区间。

线上问题还原

session1 session2
begin
begin
update test set k = 20 where id = 20
update test set k = 20 where id = 20
INSERT into test values(25,25)
INSERT into test values(25,25)

重点: insert 之前两个回话都执行完update

SQL 错误 [1213] [40001]: Deadlock found when trying to get lock; try restarting transaction



解决办法:

避免更新或者删除不存在的记录,虽然更新存在的记录也会产生间隙锁,但是间隙锁锁住的范围会更小;

更新不存在的记录会锁住意想不到的区间范围,极其容易导致死锁问题

这些仅仅是解决问题的一个小的技巧,不能从根本上解决问题,如果想从根本上解决就从代码级别上加锁,这样避免了这种问题,但是同时并发就小了,根据自己的实际情况进行定夺方案

作者:京东零售 吴法刚

来源:京东云开发者社区 转载请注明来源文章来源地址https://www.toymoban.com/news/detail-710459.html

到了这里,关于MySQL innoDB 间隙锁产生的死锁问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 越细粒度的锁越好吗?产生死锁怎么办?

    大家好,我是哪吒。 上一篇提到了锁粒度的问题,使用“越细粒度的锁越好”,真的是这样吗?会不会产生一些其它问题?

    2024年02月08日
    浏览(27)
  • 死锁的典型情况、产生的必要条件和解决方案

    死锁:多个线程同时被阻塞,他们中的一个或全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 目录 前言 一、死锁的三种典型情况 (一)一个线程一把锁 (二)两个线程两把锁 (三)N个线程M把锁 二、死锁产生的必要条件(缺一不可)

    2024年02月11日
    浏览(27)
  • Linux产生死锁的必要条件和常见的锁种类

    之前面试的时候,有面试官问我产生死锁的(4个)必要条件,这个我之前有了解过,但是当时觉得这些必要条件很官方,也就没有重视,最后答得支支吾吾,面试官笑着说下去去可以看看,这个在面试中跟智能指针一样还挺常见的;(面试全程都很顺利,卡在这个简单的问题上确

    2024年02月21日
    浏览(27)
  • MySQL遇到过死锁问题吗,你是如何解决的?

    MySQL遇到过死锁问题吗,你是如何解决的? 问题解析 死锁,就是两个或者两个以上的线程在执行过程中,去争夺同一个共享资源导致互相等待的现象。 在没有外部干预的情况下,线程会一直处于阻塞状态,无法往下执行。 要想真正产生死锁,必须同时满足四个条件。互斥条

    2024年02月02日
    浏览(38)
  • MySQL基础学习: 由delete和insert操作导致的死锁问题

    一、问题复现:表结构 二、死锁测试 (1)开启两个事务 (2)两个事务分别删除两个个不存在的记录 (3)两个事务分别插入该记录 开启事务1 开始事务2 现象描述: (1)两个事务分别执行delete操作 (2)事务1执行insert操作,阻塞 (3)事务2执行insert操作,产生死锁报错 (

    2024年02月03日
    浏览(27)
  • MySQL next-key锁引发的死锁问题分析与处理

    当方法A被多线程并发执行的时候会偶现死锁问题,分析问题: 因为业务上有限制:每次并发执行方法A的时候,每个线程执行到sql里面的参数:data_name_en有可能是一样的,但是空间#{tenantId}肯定都是不一样的,也就是并发时候,线程1要删除和插入的数据一定和线程2要删除和插

    2024年02月11日
    浏览(39)
  • Mysql与SeaweedFS数据不同步问题产生原因及解决办法

    在数据库和文件存储系统之间保持一致性是数据管理中的一个关键问题。这篇文章将专注于Mysql和SeaweedFS(一种快速、简单和高效的分布式存储系统)之间数据不同步的问题,探讨可能的原因并提出相应的解决策略。 Mysql是最流行的关系型数据库管理系统之一,被广泛应用于

    2024年02月06日
    浏览(27)
  • MySQL间隙锁是什么

    当下各行各业都卷到飞起,IT行业更是如此。对很多程序员来说,面试的难度、深度、广度,都让你承受着 \\\"你这个年纪不该承受的一切\\\"。以MySQL为例,我大学刚毕业那会,面试时也就问一下 \\\"MySQL查询怎么去重?\\\"、\\\"MySQL组函数有哪些?\\\";而现在刚毕业的学生去面试,上来就是

    2024年02月15日
    浏览(28)
  • mysql 间隙锁原理深度详解

    目录 一、前言 二、mysql之mvcc 2.1 什么是mvcc 2.2 mvcc组成 2.2.1 Undo log 多版本链 2.2.2 ReadView

    2024年02月10日
    浏览(27)
  • 聊一聊mysql中的间隙锁

    间隙锁在mysql中经常使用到,今天就聊一聊mysql的间隙锁的内容。 间隙锁是为了解决幻读的问题,并且在当前读的场景下解决的。 当前读包含:update,delete,insert,select…lock in share mode,select…for update 一基本概念 1、行锁:给某一行进行加锁 2、间隙锁:两个值之间的间隙,为解

    2024年02月12日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包