MySQL next-key锁引发的死锁问题分析与处理

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

表结构

CREATE TABLE IF NOT EXISTS `t`  (
    `id` int(11) NOT NULL,
    `data_name_en` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
    `tenant_id` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0',
    index `DE_DATADPEND_IDX`(`data_name_en`(191)) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci comment '字典和进件参数关系' ROW_FORMAT = Dynamic;

问题现象

#有个方法A会在开启事务后顺序执行下面的2个sql
    <delete id="del" >
        delete from t where data_name_en=#{dataNameEn} and tenant_id in ('-1',#{tenantId})
    </delete>
   <insert id="add" databaseId="mysql" parameterType="java.util.List">
        insert into t
        (
        id,
        data_name_en,
        tenant_id
        ) values
        <foreach collection="dataDependList" separator="," index="index" item="item">
            (
            #{item.id,jdbcType=INTEGER},
            #{item.dataNameEn,jdbcType=VARCHAR},
            #{item.tenantId,jdbcType=VARCHAR}
            )
        </foreach>
    </insert>

当方法A被多线程并发执行的时候会偶现死锁问题,分析问题:

因为业务上有限制:每次并发执行方法A的时候,每个线程执行到sql里面的参数:data_name_en有可能是一样的,但是空间#{tenantId}肯定都是不一样的,也就是并发时候,线程1要删除和插入的数据一定和线程2要删除和插入的数据肯定是不一样的,那么为什么还会发生死锁呢。

结合以前看过的课程:极客时间《MySQL实战45讲:20 | 幻读是什么,幻读有什么问题?》《21 | 为什么我只改一行的语句,锁这么多?》,把这两篇完全看懂就能解释清楚问题原因了。MySQL在执行上述delete语句的时候,执行的是当前读,就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁;也就是为了避免事务期间执行delete语句删除的数据前后不一致,MySQL肯定是要加锁才能保证的。观察delete语句的where条件,data_name_en字段有一个普通索引,tenant_id 没有带索引,不是唯一索引,MySQL无法采用行锁来解决事务执行过程中会有其他线程新insert进去的记录也是符合删除where条件的情况,那么必须加上某种锁来满足MySQL默认的事务隔离级别(RR可重复读),也就是加上锁那么在同一个事务中重复执行那条delete语句影响的结果集都是固定的,否则就会出现幻读问题。那么MySQL会加什么锁来解决这种问题呢?

InnoDB 存储引擎通过 next-key 锁(记录锁和间隙锁的组合)来锁住记录本身和记录之间的“间隙”,防止其他事务在这个记录之间插入新的记录,从而避免了幻读现象。对记录加锁带基本单位是 next-key 锁,但是会因为一些条件会退化成间隙锁,或者行锁。加锁的位置准确的说,锁是加在索引上的而非行上。

加锁规则

  • 原则 1:加锁的基本单位是 next-key lock。next-key lock 是前开后闭区间。
  • 原则 2:查找过程中访问到的对象才会加锁。
  • 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
  • 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
  • 一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

加何种锁与where条件的字段是否有索引以及是否是唯一索引以及where条件的数据是否存在有关,比如:

在 update 语句的 where 条件使用了唯一索引,那么 next-key 锁会退化成记录锁,也就是只会给一行记录加锁。在 update 语句的 where 条件没有使用索引,就会对所有记录和间隙加上 next-key 锁(记录锁 + 间隙锁),相当于把整个表锁住了。

本例中,where条件有一个字段有普通索引,那么接下来使用工具观察sql是否加了锁,加的什么锁。

先保证t 表有初始数据,执行下面的sql:

insert into t(id,data_name_en,tenant_id )values(-1,'a','1');
insert into t(id,data_name_en,tenant_id )values(-2,'b','1');
insert into t(id,data_name_en,tenant_id )values(-3,'a','2');
insert into t(id,data_name_en,tenant_id )values(-4,'b','2');

然后使用Navicat打开两个窗口,

窗口1:session A 窗口2:session B
T1 begin;delete from t where data_name_en=‘a’ and tenant_id in (‘1’);
T2 begin;delete from t where data_name_en=‘b’ and tenant_id in (‘2’);
T3 insert into t (id,data_name_en,tenant_id )values(-5,‘b’,‘1’);
T4 insert into t (id,data_name_en,tenant_id )values(-6,‘a’,‘2’);

执行T3的时候会发现执行会卡住,在等待T2的事务提交,说明前面执行的的sql加了锁,然后执行:

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

可以观察到lock_mode列:X,GAP,说明等待的锁是X排他锁和GAP间隙锁;

如果继续执行t4,会报:Deadlock found when trying to get lock; try restarting transaction,说明发生了死锁

那么间隙锁是如何导致死锁现象发生的呢?

首先根据加锁规则,T1和T2的delete操作的where条件有非唯一索引,那么加的是next-key lock,其实接下来都不用仔细分析到底加锁的范围是什么,因为这是锁的一个范围,那么很明显就可能会与另一个线程的insert产生冲突了。

以下举例一种情况:并发执行期间,线程A执行T1 的delete的时候,间隙锁锁了数据范围A;线程B执行T2的delete的时候,间隙锁锁了范围B,间隙锁与间隙锁之间是不冲突的,所以都可以执行成功;然后A线程执行T3的insert的时候插入的数据(data_name_en=‘b’)正好满足线程B的锁范围B,要等待B提交事务释放锁,然后线程B执行T4的insert的时候插入的数据正好满足线程A的锁范围A,也要等待A提交事务释放锁;双方都在等待对方先释放锁导致死锁问题产生;当然,实际情况是多个线程执行上述业务,可能是A等待B,B等待C,C又等待A

解决方案

总体思路:解决死锁就要破坏产生死锁的必要条件

1.把delete语句修改为先根据where条件查询出主键id(id如果不是主键需要先改为主键),然后根据id来进行删除,由于是根据目前已经存在的id删除,根据上面加锁规则,主键id的等值查询加的是行锁。且业务逻辑决定了并发进行删除的id也一定是不一样的,而且由于insert的id是递增的主键,那么肯定和delete的id也是不同的,这样delete之间以及delete和insert之间都没有锁冲突,不存在争抢锁资源的情况,死锁问题得以解决文章来源地址https://www.toymoban.com/news/detail-511887.html

到了这里,关于MySQL next-key锁引发的死锁问题分析与处理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 记录一次mysql死锁日志分析

    记录一次mysql死锁日志分析

    记录一次mysql死锁-CSDN博客 MySQL死锁日志的查看和分析_mysql死锁日志解读_lkforce的博客-CSDN博客 此文承接以上两篇文章,文章1原创记录,文章2转载分析 一,死锁sql update tt_task          SET navigation_distance = ?,    plan_arrive_time = ?          where id = ? update tt_task set grabbing_status

    2023年04月15日
    浏览(44)
  • MySQL性能测试及调优中的死锁处理方法

    MySQL性能测试及调优中的死锁处理方法

    以下从死锁检测、死锁避免、死锁解决3个方面来探讨如何对MySQL死锁问题进行性能调优。 死锁检测 通过SQL语句查询锁表相关信息: (1)查询表打开情况 1 (2)查询锁情况列表 1 (3)查询锁等待信息,其中blocking_lock_id是当前事务在等待的事务 1 (4)查询死锁日志 SHOW ENGI

    2024年01月17日
    浏览(10)
  • MySQL事务死锁问题排查

    MySQL事务死锁问题排查

    在预发环境中,由消息驱动最终触发执行事务来写库存,但是导致MySQL发生死锁,写库存失败。 初步排查,在同一时刻有两条请求进行写库存的操作。 时间前后相差1s,但最终执行结果是,这两个事务相互死锁,均失败。 事务定义非常简单,伪代码描述如下: 该数据库表的

    2024年02月08日
    浏览(10)
  • mysql死锁分析show engine innodb status

    mysql死锁分析show engine innodb status

    最近在使用mysql的show engine innodb status命令分析死锁,发现了一个有意思的点。就是红框里圈出来的这个。 红框的数据所代表的是sql语句需要获取的行锁。实际上,这个值应该是-1才对,很纳闷,为什么会展示7fffffff呢?其实这个事和innodb的一条关于有符号数的规定有关系。

    2024年02月13日
    浏览(8)
  • 这问题巧了,SpringMVC 不同参数处理机制引发的思考

    这个问题非常有趣,不是SpringMVC 的问题,是实际开发中混合使用了两种请求方式暴露出来的。 功能模块中,提供两个 Http 服务。一个是列表查询(application/json 请求),一个是列表导出(表单请求)。运行环境发现个问题:MVC model 新添加的属性,类似的 Http 请求,一个有值

    2024年02月11日
    浏览(13)
  • java八股文面试[数据库]——MySQL死锁的原因和处理方法

    java八股文面试[数据库]——MySQL死锁的原因和处理方法

    1) 表的死锁 产生原因 : 用户A访问表A(锁住了表A),然后 又访问表B ;另一个用户B访问表B(锁住了表B),然后企图 访问表A ;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。 用户A--》A表(表

    2024年02月09日
    浏览(15)
  • MySQL innoDB 间隙锁产生的死锁问题

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

    2024年02月08日
    浏览(10)
  • Slave SQL线程与PXB FTWRL死锁问题分析

    Slave SQL线程与PXB FTWRL死锁问题分析

    2.27号凌晨生产环境MySQL备库在执行备份期间出现因FLUSH TABLES WITH READ LOCK未释放导致备库复制延时拉大,慢日志内看持锁接近25分钟未释放。 版本: MySQL 5.7.21 PXB 2.4.18 慢查询日志: 备份脚本中的备份命令: mysql_kill.sh的主要逻辑内容: 备份参数: 144是SQL线程,并行复制中的

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

    MySQL遇到过死锁问题吗,你是如何解决的?

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

    2024年02月02日
    浏览(11)
  • 一次不规范HTTP请求引发的nginx响应400问题分析与解决

    一次不规范HTTP请求引发的nginx响应400问题分析与解决

    最近分析数据偶然发现nginx log中有一批用户所有的HTTP POST log上报请求均返回400,没有任何200成功记录,由于只占整体请求的不到0.5%,所以之前也一直没有触发监控报警,而且很奇怪的是只对于log上报的POST接口会存在这种特定用户全部400的情况,而对于其他接口无论POST还是

    2024年02月14日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包