slave开启MTS时执行mysqldump引发死锁案例(1)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: slave开启MTS时执行mysqldump引发死锁案例

一、问题来源

这是一位客户的提供的案例如下,show processlist截图如下:

屏幕快照 2021-11-19 上午12.09.38.png

出现这种问题除非手动干预,杀掉FTWRL的session,复制线程方可以继续进行。版本社区版5.7.26。


二、堵塞图

如果分析上面的堵塞可以画图如下:

屏幕快照 2021-11-19 上午12.09.09.png


三、关于woker线程w1和w3的等待

这里我们需要重点关注参数 slave_preserve_commit_order,在我将要出版的《深入理解MySQL主从原理》一书中做了详细描述,这里简单说明如下:

  • 这个参数是为了保证从库 group commit 中的每个工作线程的事务提交顺序和主库事务执行的顺序一致。它在 order commit 的flush阶段前就生效。工作线程的事务在等待获取自己提交权限期间会堵塞在状态 ‘Waiting for preceding transaction to commit’ 下。

但是我们知道在order commit的flush之前就会获取 MDL_key::COMMIT。因此这里w1和w3工作线程正在等待自己提交权限的到来,但是遗憾的是w2的事务由于不能获取 global read lock 而迟迟不能提交。同时它们堵塞了FTWRL。


四、关于FTWRL的等待

这个我也多次描述过了,FTWRL的过程大概如下:

第一步: 加MDL LOCK类型为GLOBAL,级别为S。如果出现等待状态为 ‘Waiting for global read lock’。注意select语句不会上GLOBAL级别上锁,但是DML/DDL/FOR UPDATE语句会上GLOBAL级别的IX锁,IX锁和S锁不兼容会出现这种等待。下面是这个兼容矩阵:

          | Type of active   |
  Request |   scoped lock    |
   type   | IS(*)  IX   S  X |
 ---------+------------------+
 IS       |  +      +   +  + |
 IX       |  +      +   -  - |
 S        |  +      -   +  - |
 X        |  +      -   -  - |



第二步: 推进全局表缓存版本。源码中就是一个全局变量 refresh_version++。 第三步: 释放没有使用的table 缓存。可自行参考函数 close_cached_tables。 第四步: 判断是否有正在占用的table缓存,如果有则等待,等待占用者释放。等待状态为 'Waiting for table flush'。这一步会去判断table缓存的版本和全局表缓存版本是否匹配,如果不匹配则等待如下:

for (uint idx=0 ; idx < table_def_cache.records ; idx++)

{
share= (TABLE_SHARE*) my_hash_element(&table_def_cache, idx); //寻找整个 table cache shared hash结构
if (share->has_old_version()) //如果版本 和 当前 的 refresh_version 版本不一致
{
found= TRUE;
break; //跳出第一层查找 是否有老版本 存在
}
}
...
if (found)//如果找到老版本,需要等待
{
/*
The method below temporarily unlocks LOCK_open and frees
share's memory.
*/
if (share->wait_for_old_version(thd, &abstime,
MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))
{
mysql_mutex_unlock(&LOCK_open);
result= TRUE;
goto err_with_reopen;
}
}

而等待的结束就是占用的table缓存的占用者释放,这个释放操作存在于函数 close_thread_table中,如下:

if (table->s->has_old_version() || table->needs_reopen() ||
table_def_shutdown_in_progress)
{
tc->remove_table(table);//关闭 table cache instance
mysql_mutex_lock(&LOCK_open);
intern_close_table(table);//去掉 table cache define
mysql_mutex_unlock(&LOCK_open);
}



最终会调用函数 MDL_wait::set_status 将 FTWRL 唤醒,也就是说对于正在占用的table缓存释放者不是FTWRL会话而是占用者自己。不管怎么样最终整个table缓存将会被清空,如果经过FTWRL后去查看 Open_table_definitions 和 Open_tables 将会发现重新计数了。下面是唤醒函数的代码,也很明显:


bool MDL_wait::set_status(enum_wait_status status_arg) open_table
{
bool was_occupied= TRUE;
mysql_mutex_lock(&m_LOCK_wait_status);
if (m_wait_status == EMPTY)
{
was_occupied= FALSE;
m_wait_status= status_arg;
mysql_cond_signal(&m_COND_wait_status);//唤醒
}
mysql_mutex_unlock(&m_LOCK_wait_status);//解锁
return was_occupied;
}



第五步: 加MDL LOCK类型COMMIT 级别为S。如果出现等待状态为 ‘Waiting for commit lock’。如果有大事务的提交很可能出现这种等待。

注意 这里的第五步,正是因为w1和w3获取了 MDL LOCK COMMIT,而又在等待w2的事务提交因此FTWRL也不得不等待。



五、关于woker线程w2的等待

这里可能的原因有2个:

  • 多线程并行的情况下,线程执行的顺序本生就是不定的,很可能线程由于丢失CPU而落后其他线程的处理,因为CPU调度的最小单位是线程。如果保证某个共享内存操作的完整性需要用到mutex、原子变量等技术。
  • 如果w2中的事务本生就包含了多个DML语句,那么获取 GLOBAL READ LOCK 本身就是间歇性的,也就是每个语句结束都会释放,然后下一个语句开始的时候再次open table来获取。

我们来看看第二点,只考虑row_format格式的binlog。

我们知道一个事务可以包含多个语句,每条语句都会包含一个map Event和多个DML Event,当本Event是语句的最后一个Event的时候会使用STMT_END_F进行标记,也正是在这个时候会释放 GLOBAL READ LOCK,源码有如下:

if (get_flags(STMT_END_F))
{
if((error= rows_event_stmt_cleanup(rli, thd)))

栈:
#0 MDL_context::release_lock (this=0x7fffa8000a08, duration=MDL_STATEMENT, ticket=0x7fffa800ea40) at /opt/percona-server-locks-detail-5.7.22/sql/mdl.cc:4350
#1 0x0000000001464bf1 in MDL_context::release_locks_stored_before (this=0x7fffa8000a08, duration=MDL_STATEMENT, sentinel=0x0) at /opt/percona-server-locks-detail-5.7.22/sql/mdl.cc:4521
#2 0x000000000146541b in MDL_context::release_statement_locks (this=0x7fffa8000a08) at /opt/percona-server-locks-detail-5.7.22/sql/mdl.cc:4813
#3 0x0000000001865c75 in Relay_log_info::slave_close_thread_tables (this=0x341e8b0, thd=0x7fffa8000970) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_rli.cc:2014
#4 0x0000000001865873 in Relay_log_info::cleanup_context (this=0x341e8b0, thd=0x7fffa8000970, error=false) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_rli.cc:1886
#5 0x00000000017e8fc7 in rows_event_stmt_cleanup (rli=0x341e8b0, thd=0x7fffa8000970) at /opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:11782
#6 0x00000000017e8c79 in Rows_log_event::do_apply_event (this=0x7fffa8017dc0, rli=0x341e8b0) at /opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:11660
#7 0x00000000017cfdcd in Log_event::apply_event (this=0x7fffa8017dc0, rli=0x341e8b0) at /opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:3570
#8 0x00000000018476dc in apply_event_and_update_pos (ptr_ev=0x7fffec14f880, thd=0x7fffa8000970, rli=0x341e8b0) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_slave.cc:4766
#9 0x0000000001848d9a in exec_relay_log_event (thd=0x7fffa8000970, rli=0x341e8b0) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_slave.cc:5300
#10 0x000000000184f9cc in handle_slave_sql (arg=0x33769a0) at /opt/percona-server-locks-detail-5.7.22/sql/rpl_slave.cc:7543
(gdb) p ticket->m_lock->key.mdl_namespace()
$1 = MDL_key::GLOBAL
(gdb) p ticket->m_type
$2 = MDL_INTENTION_EXCLUSIVE
(gdb) p ticket->m_duration
$3 = MDL_STATEMENT


如果下一条语句开始又会重新获取GLOBAL READ LOCK,这就是我说的间歇性获取。


到这里死锁条件已经成熟,只要遇到这种情况就可能需要人为介入才能继续了。


            </div>
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
SQL 缓存 关系型数据库
slave开启MTS时执行mysqldump引发死锁案例(1)
slave开启MTS时执行mysqldump引发死锁案例
slave开启MTS时执行mysqldump引发死锁案例(1)
|
SQL 关系型数据库 MySQL
slave开启MTS时执行mysqldump引发死锁案例(2)
slave开启MTS时执行mysqldump引发死锁案例
286 0
|
安全 网络安全 数据安全/隐私保护
想哭(WannaCry)勒索病毒的用户防护和处置指南
本文讲的是想哭(WannaCry)勒索病毒的用户防护和处置指南,北京时间2017年5月12日晚,勒索软件"WannaCry"感染事件爆发,全球范围内99个国家遭到大规模网络攻击,被攻击者电脑中的文件被加密,被要求支付比特币以解密文件;众多行业受到影响,比如英国的NHS服务,导致至少40家医疗机构内网...
2105 0
|
敏捷开发 安全 Go
Deliver projects in 100% of the time with SAP S/4 HANA
Deliver projects in 100% of the time with SAP S/4 HANA on Project Decision Option (On-premise vs Cloud) https://www.
1055 0
|
Java Windows
运行java.exe 出现Registry key 'Software/JavaSoft/Java Runtime Environment/CurrentVersion'错误的解决。(原创)
前提:配置好了Path和ClassPath环境变量。 在控制台执行java.exe的时候多次出现如下错误: Registry key 'Software/JavaSoft/Java Runtime Environment/CurrentVersion'has value '1.
1603 0
|
11天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
7天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2514 17
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
7天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1520 14
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
3天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
9天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
555 14