iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >如何理解MySQL升级WRITE_SET后死锁的产生
  • 412
分享到

如何理解MySQL升级WRITE_SET后死锁的产生

2024-04-02 19:04:59 412人浏览 泡泡鱼
摘要

这篇文章将为大家详细讲解有关如何理解Mysql升级WRITE_SET后死锁的产生,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。背景mysql在推出MGR的时

这篇文章将为大家详细讲解有关如何理解Mysql升级WRITE_SET后死的产生,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

背景

mysql在推出MGR的时候使用了WRITE_SET, 借用这个思想, Mysql在5.7.22版本引入了基于WRITE_SET的并行复制方案[1]。在原先的主从复制技术中,同一批次的事物能进入事物的prepare阶段说明那批事物没有冲突,所以可以并发执行。我们都知道innodb是基于行锁的数据库,所以如果能够按照行级别的粒度来并发的回放数据会对性能有很大的提高。采用这套方案的性能优点就有很多方面了,其中一个可以简单看到的好处就是:我们在回放的时候就不用依赖于主上事物提交的情况了,正所谓less is more。减少了依赖,并行从宏观上也能按照逻辑行这样的来回放,所以性能肯定有很大的提升[2]. 故而,我们数据库这边在一些实例上启用了这个并行回放特性。

导致我们死锁的现象是: 我们发现开启了write_set并行回放的实例从库上死锁的概率比以前高了不少, 并且发生死锁的实例都是在进行xtrabackup备份。

场景

我们知道MySQL事物会设计到很多的锁,比如MDL锁,innodb的行锁,意向锁,latch 锁等等。不同的隔离级别锁的行为也有很多的差异。从死锁理论的角度:死锁就是有向图中存在环,从而造成相互等待。要解决死锁只要简单的破坏任何一条边,来打破环行等待。当然实际的实现可能会因各个环节点的权重不同而有所优化,选择代价最小的。但之前的重点肯定是找出这个“环”。而这些锁有些是运维的时候可以看到有些是看不到的。比如latch锁一般对用户看不到。因为性能原因,我们的MDL锁和INNODB锁的详细信息并未收集。如果开启了,就可以通过perfORMance_schema.metadata_lock这个表来查询MDL锁的相关信息,通过show engine innodb status来查看详细innodb的加锁信息。

通过简单的分析,我们锁定是MDL死锁。所以在这样的场景下,我们只能通过show full processlist来查看到当时的状态,如下图:

case1: 如何理解MySQL升级WRITE_SET后死锁的产生 图1

case2: 如何理解MySQL升级WRITE_SET后死锁的产生 图2-1

如何理解MySQL升级WRITE_SET后死锁的产生 图2-2

===

为了方便大家理解, 我画了一个示意图[图3]来解释这两个case的死锁情况: 如何理解MySQL升级WRITE_SET后死锁的产生 图3

case1 死锁分析:

可以看到在work线程组中,有一个work处理的事物先到达了事物的提交状态, 但是事物在提交前需要进行 order_commit判断,因为我们设置了slave_preserve_commit_order ,要保证事物是按照主库上的提交顺序来提交的。所以这个时候必须等待之前的事物要提交才可以进行。所以看到这个线程的状态是: "Waiting for preceding transaction to commit"。当那个"靠前"的事物准备提交的时候要去拿mdl::commit_lock这把锁,发现要不到。形成如上的“环等待”。

通过分析可以知道,这个时候同时执行了 FTWRL (flush table with read lock), 而这个操作会获取到MDL的一个共享锁。但是同样没有版本获取mdl::commit_lock 而等待。这个等待会造成新来的更新请求被阻塞,因为更新的语句是排他类型的锁。由于篇幅的原因,不细说MDL锁兼容细节。这里只给出结论,会阻塞部分更新的语句,进而会影响到业务。

=== 如何理解MySQL升级WRITE_SET后死锁的产生 图4

case2 死锁分析:

顺便提一句: 同样可以看到,这种情况下新的请求被阻塞主。注意,这也正是备份的核心思想。阻塞新来的请求,阻塞同批次的提交。保证在备份的时候没有新的数据插入

一开始一个比较"靠后"的事物获取了mdl::commit_lock,在准备提交的时候,发现系统配置了slave_preserve_commit_order,同时该事物的前面还有事物未提交,需要等待前面的事物先执行完成后才能继续。然后FTWRL先获取了mdl::global_read_lock锁,但是没有办法获取mdl::commit_lock锁。

这个时候如果这个“前面的事物”是更新操作,那么就跟mdl::global_read_lock锁互斥,故而形成上面的死锁。

验证

由于这样的死锁,是概率出现的。为了高效的复现问题,我们打算使用mysql的测试框架来验证. 第一个步骤是:通过上面的分析,修改内核源码加大死锁的概率。证明我们的猜想确实能够出现死锁。但是这个出现的死锁并不一定就是线上真是环境的死锁。故而需要我们把修改的源码在实际场景下面验证。当然我们没有办法在生产环境来验证。我们可以通过第一步修改的源码,然后使用备份的数据来模拟。如果使用备份的数据 + 我们修改的源码数据库实例复现了,才能客观的判断我们的死锁研判。当然读者可能说我们修改源码破坏了之前的环境,这里当然是有前提的。这个前提就是:只修改并行回放线程组中的某一个线程,不改变原有逻辑,只是单纯的让它支持慢一点来提高死锁的概率,作证我们的死锁研判。

首先我们的第一步就是要:在主库上产生两个事物(当然我们也可以使用蛮力,循环,不过可能效果差,甚至可能无法复现),使用MySQL的测试框架,祥见如下的代码:

57 #  ===========================
58 # 在master上创建两个链接master和master1
59 --source include/rpl_connection_master.inc
60 send SET DEBUG_SYNC='waiting_in_the_middle_of_flush_stage SIGNAL w WAIT_FOR b';
61
62 --source include/rpl_connection_master1.inc
63 send SET DEBUG_SYNC= 'now WAIT_FOR w';
64
65 --source include/rpl_connection_master.inc
66 --reap
67 show master status;
68 send insert into test.t1 values(1);
69
70 --source include/rpl_connection_master1.inc
71 --reap
72 SET DEBUG_SYNC= 'bGC_after_enrolling_for_flush_stage SIGNAL b';
73 insert into test.t1 values(1000);

如何验证我们的主库上这两个事物属于同一个批次呢?当然是binlog啦。结果如下:

show master status;
File	Position	Binlog_Do_DB	Binlog_Ignore_DB	Executed_Gtid_Set
master-bin.000001	849#200107  9:26:14 server id 1  end_log_pos 219 CRC32 0x059fa77a 	Anonymous_GTID	last_committed=0	sequence_number=1	rbr_only=no#200107  9:26:24 server id 1  end_log_pos 408 CRC32 0xa1a6ea99 	Anonymous_GTID	last_committed=1	sequence_number=2
	rbr_only=yes#200107  9:26:24 server id 1  end_log_pos 661 CRC32 0x2b0fc8a5 	Anonymous_GTID	last_committed=1	sequence_number=3	rbr_only=yes

可以看到last_commit这个字段我们一共产生了两组binlog, 一个是0 这里是create table 语句。另外一个是1, 就是我们上面的两条insert 语句。

接下来就是就是要修改MySQL的源代码了,这里主要是要考虑到MTS的并行复制逻辑。因为我们在主库上通过DEBUG_SYNC让大的事物先执行,所以比如是大的事物先分配到woker线程组中的第一个。所以我们在binlog回放的关键路径上: Xid_apply_log_event::do_apply_event_worker 这个函数中让第一个worker sleep足够多的时间让我们执行FTWRL。

直接修改源代码编译需要来回的编译,我们这边使用systemstap 这个工具,JIT在运行时注入一段代码来改变某些worker的行为。在执行注入前先执行脚本验证下能否注入:

41 --exec sudo stap -L 'process("$MYSQLD").function("pop_jobs_item")'
42 --exec sudo stap -L 'process("$MYSQLD").function("*Xid_apply_log_event::do_apply_event_worker")'

需要注意的是,因为stap的架构原理的原因,详细可参考下面的链接[3],需要root权限。下面是注入的代码:

stap -v -g -d $MYSQLD --ldd -e 'probe process($server_pid).function("Xid_apply_log_event    ::Xid_apply_log_event
") {printf("hit in do_apply_log_event\n") if ($w->id ==0) { mdelay(30000)} }'
stap -v -g -d $MYSQLD --ldd -e 'probe process($server_pid).function("pop_jobs_item") { printf("hit in
pop_jobs_item") if ($worker->id == 0) { mdelay(3000)} }'

大致的意思就是: 让复制线程组的第一个线程sleep 3s。这样有足够的时间来运行FTWRL。最终的执行结果:

show full processlist;
Id	User	Host	db	Command	Time	State	Info
3	root	localhost:10868	test	Sleep	83		NULL
4	root	localhost:10870	test	Sleep	84		NULL
7	root	localhost:10922	test	Query	61	Waiting for commit lock	flush table with read lock
8	root	localhost:10926	test	Query	0	starting	show full processlist
9	system user		NULL	Connect	82	Waiting for master to send event	NULL
10	system user		NULL	Connect	61	Slave has read all relay log; waiting for more updates	NULL
11	system user		NULL	Connect	71	Waiting for global read lock	NULL
12	system user		NULL	Connect	71	Waiting for preceding transaction to commit	NULL
13	system user		NULL	Connect	82	Waiting for an event from Coordinator	NULL
14	system user		NULL	Connect	81	Waiting for an event from Coordinator	NULL

可以看到,我们的猜想完整的复现了死锁。大致解释下:

我们在构造这个死锁的时候,因为我们控制 的worker会sleep 3s。故而我们可以查询worker的状态,当worker处于 Waiting for preceding transaction to commit 这个状态的时候,立马执行FTWRL。然后可以看到FTWRL会block在commit_lock。然后另外一个更新自然是要等待: global read lock, 而形成死锁。

首先对于不太理解备份原理的同学,应该可以从这两个死锁等待图中清楚的看到FTWRL的作用。它是通过两把GLOBAL READ LOCK 和COMMIT_LOCK锁来控制备份的一致性。这里不详细讨论。 解决死锁问题,通过死锁理论,肯定是要打破有向图中的环。

在我们的这个死锁case中通过分析可以知道可以操作的两条边只有: 

1. slave_preserve_commit_order
2. FTWRL 显然:对于那些可以接受在从库上事物的提交可以“乱序”的,我们只要关闭这个配置选项就可以解除死锁

而如果是要强制要求有序的,那么我们只能关闭备份的线程(图中的节点,及相关的边) 同样可以破解死锁。在死锁出现的时候,个人觉得关闭备份线程代码是更小的。如果关闭worker线程的话,从库复制会出错误。

关于如何理解MySQL升级WRITE_SET后死锁的产生就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

您可能感兴趣的文档:

--结束END--

本文标题: 如何理解MySQL升级WRITE_SET后死锁的产生

本文链接: https://www.lsjlt.com/news/61517.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何理解MySQL升级WRITE_SET后死锁的产生
    这篇文章将为大家详细讲解有关如何理解MySQL升级WRITE_SET后死锁的产生,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。背景MySQL在推出MGR的时...
    99+
    2024-04-02
  • mysql如何产生死锁的
    这篇文章将为大家详细讲解有关mysql如何产生死锁的,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。mysql中死锁<DeadLock>:是指两个或两个以上的进...
    99+
    2024-04-02
  • mysql死锁是如何产生的
    这篇文章主要介绍mysql死锁是如何产生的,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!mysql是一种关系型数据库管理系统,使用的 SQL 语言是用于访问数据库的最常用标准化语言。...
    99+
    2024-04-02
  • MySQL - 死锁的产生及解决方案
    MySQL - 死锁的产生及解决方案 1. 死锁与产生死锁的四个必要条件1.1 什么是死锁1.2 死锁产生的4个必要条件 2. 死锁案例2.1 表锁死锁2.2 行锁死锁2.3 共享锁转换为排他锁 3. 死锁排查4. 实例分析...
    99+
    2023-08-17
    mysql 数据库
  • MySQL死锁产生的原因和解决方法
    前言 最近老顾经常碰到同事说,mysql又死锁了导致业务报错。今天我们就来聊聊死锁以及怎么解决 锁类型 mysql锁级别:页级、表级、行级 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 行级锁:...
    99+
    2023-08-31
    mysql 数据库 java
  • 数据库死锁是如何产生的
    这篇文章主要介绍了数据库死锁是如何产生的,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了解一下。死锁(Deadlock)所谓死锁:是指两个或两个以上的进程...
    99+
    2024-04-02
  • 如何解决mysql主从复制中产生了锁的问题
    这篇文章给大家分享的是有关如何解决mysql主从复制中产生了锁的问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一套主主复制的mysql库产生了死锁,导致主从同步出现问题, ...
    99+
    2024-04-02
  • 如何理解MYSQL RC模式insert update可能死锁的情况
    本篇文章给大家分享的是有关如何理解MYSQL RC模式insert update可能死锁的情况,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。 ...
    99+
    2024-04-02
  • 如何解决MySQL的RR模式下死锁一列
    如何解决MySQL的RR模式下死锁一列,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。环境:版本5.7.29 RR隔离级别一、案例模拟CRE...
    99+
    2024-04-02
  • 如何理解mysql的锁机制
    本篇文章为大家展示了如何理解mysql的锁机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。 锁0.1 锁机制当前MySQL已经支持 ISAM, MyIS...
    99+
    2024-04-02
  • 如何使用不同的索引更新解决MySQL死锁
    这篇文章给大家分享的是有关如何使用不同的索引更新解决MySQL死锁的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。示例如下CREATE TABLE `t3`&n...
    99+
    2024-04-02
  • 如何实现MySQL底层优化:事务锁的高级性能优化和避免死锁的方法
    如何实现MySQL底层优化:事务锁的高级性能优化和避免死锁的方法引言:在数据库系统中,事务锁是保证数据一致性和并发访问的重要机制之一。然而,在高并发场景下,事务锁可能会导致性能问题和死锁。为了提高MySQL性能,我们需要对事务锁进行高级性能...
    99+
    2023-11-08
    死锁 MySQL底层优化关键词:事务 锁优化
  • 如何解决升级IDEA后Lombok不能使用的问题
    本篇内容介绍了“如何解决升级IDEA后Lombok不能使用的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!今天到工作室比较晚,在电脑前吃...
    99+
    2023-06-20
  • 如何解决升级内核后出现initramfs错误的现象
    本篇内容主要讲解“如何解决升级内核后出现initramfs错误的现象”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何解决升级内核后出现initramfs错误的现象”吧!出现initramfs找...
    99+
    2023-06-10
  • 如何理解mysql innodb lock锁中的record lock
    本篇文章给大家分享的是有关如何理解mysql innodb lock锁中的record lock,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。...
    99+
    2024-04-02
  • 如何处理生产线MySQL库Binlog日志占满磁盘的故障
    本篇内容主要讲解“如何处理生产线MySQL库Binlog日志占满磁盘的故障”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何处理生产线MySQL库Binlog日...
    99+
    2024-04-02
  • 如何理解MySQL的锁和并发控制技术?
    如何理解MySQL的锁和并发控制技术?MySQL是一种常用的关系型数据库管理系统,它支持并发访问和操作数据,同时也提供了一些锁和并发控制技术,以保证数据的一致性和并发性。本文将详细介绍MySQL的锁和并发控制技术,并通过代码示例来加深理解。...
    99+
    2023-10-22
    MySQL 并发控制
  • 如何解决Android studio升级到3.0之后运行时的问题示例
    这篇文章主要为大家展示了“如何解决Android studio升级到3.0之后运行时的问题示例”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何解决Android studio升级到3.0之后运...
    99+
    2023-05-30
    android studio
  • 如何理解MySQL中的事务隔离级别
    这篇文章给大家介绍如何理解MySQL中的事务隔离级别,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。很多小伙伴对 MySQL 的隔离级别一直心存疑惑,其实这个问题一点都不难,关键看怎么讲...
    99+
    2024-04-02
  • 在AmazonAurora中如何管理数据库实例的生命周期和版本升级
    在Amazon Aurora中,可以通过使用Amazon RDS控制台或AWS CLI来管理数据库实例的生命周期和版本升级。 创建...
    99+
    2024-04-09
    AmazonAurora
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作