最近线上执行备份的从库时出现复制卡死现象,分析以后发现是两个死锁,show full processlist的状态如图1所示,其中,数据库版本是官方5.7.18版本,我们内部做了些许修改,但与此次死锁无关。

图一

先说一下结论,图一中:

162线程是执行innobackup执行的flush tables with read lock;

144是SQL线程,并行复制中的Coordinator线程;

145/146是并行复制的worker线程,145/146worker线程队列中的事务可以并行执行。

144Coordinator线程分发relaylog中事务时发现这个事务不能执行,要等待前面的事务完成提交,所以处于waiting for dependent transaction to commit的状态。145/146线程和备份线程162形成死锁,145线程等待162线程 global read lock 释放,162线程占有MDL::global read lock 全局读锁,申请全局commit lock的时候阻塞等待146线程,146线程占有MDL:: commit lock,因为从库设置slave_preserve_commit_order=1,保证从库binlog提交顺序,而146线程执行事务对应的binlog靠后面,所以等待145的事务提交。最终形成了145->162->146->145的死循环,形成死锁。

同样的,图二中:

183是备份程序执行的flush tables with read lock;

165是SQL线程,并行复制的Coordinator线程;

166/167是并行复制的worker线程。

图二

165Coordinator线程分发的事务还不能执行,进入waiting for dependent transaction to commit的状态,183、166、167三个线程形成死锁,183占有全局读锁,获取全局commit锁的时候进入阻塞,等待167释放事务涉及到表的commit锁;166,167的事务可以并行复制,167占有表级commit锁,但是事务对应的binlog在后面,阻塞等待166先提交进入waiting for preceding transaction to commit的状态;166线程事务执行时提交要获得表级commit锁,但已经被183占有,所以阻塞等待。这样形成了183->167->166->183的死锁。

三个线程相互形成死锁,在我的经验中还是很少见的,又因为涉及的MDL锁是服务层的锁,死锁检测也不会起作用。

死锁原因分析

1、MDL锁

参考:http://mysql.taobao.org/monthly/2015/11/04/

2、flush tables with read lock获取两个锁

MDL::global read lock 和MDL::global commit lock,而且是显示的MDL_SHARED锁。

//Global_read_lock::lock_global_read_lock

MDL_REQUEST_INIT(&mdl_request,MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT);

//Global_read_lock::make_global_read_lock_block_commit

MDL_REQUEST_INIT(&mdl_request,MDL_key::COMMIT, "", "", MDL_SHARED, MDL_EXPLICIT);

3、事务执行中涉及两个锁

在所有更新数据的代码路径里,除了必须的锁外,还会额外请求MDL_key::GLOBAL锁的MDL_INTENTION_EXCLUSIVE锁;在事务提交前,会先请求MDL_key::COMMIT锁的MDL_INTENTION_EXCLUSIVE锁。对于scope锁来说,IX锁和S锁是不兼容的。

4、--slave_preserve_commit_order

For multi-threaded slaves, enabling this variable ensures that

transactions are externalized on theslave in the same order as they appear

in the slave's relay log.

slave_preserve_commit_order=1时,relay-log中事务的提交顺序会严格按照在relay-log中出现的顺序提交。

所以,事务的执行和flush tables with read lock语句获得两个锁都不是原子的,并行复制时模式下按以下的顺序就会出现死锁。

事务A、B可以并行复制,relay-log中A在前,slave_preserve_commit_order=1

从库回放时B事务执行较快,先执行到commit,获得commit锁,并进入waiting for preceding transaction to commit的状态

执行flush tables with read lock,进入waiting for commit的状态

事务A执行。事务A如果在FTWRL语句获得global read lock锁之后执行,那么事务A就进入waiting for global read lock的状态,即第一种死锁;如果事务A在FTWRL获得global read lock之前执行,同时FTWRL获得global commit锁之后应用Xid_event提交事务,则进入 waiting for the commit lock的状态,即第二种死锁。

复现

理解了死锁出现的原因后,重现就简单多了。重现这个死锁步骤主要是2步:

1、在主库构造并行复制的事务,利用debug_sync

session 1

SET DEBUG_SYNC='waiting_in_the_middle_of_flush_stage SIGNAL s1 WAIT_FOR f';

insert into test.test values(13);//事务A

//session 2

SET DEBUG_SYNC= 'now WAIT_FOR s1';

SET DEBUG_SYNC= 'bgc_after_enrolling_for_flush_stage SIGNAL f';

insert into test.test values(16);//事务B

2、从库执行,修改源代码,在关键地方sleep若干时间,控制并行复制的worker的执行并留出足够时间执行flush tables with read lock

修改点如下:

//Xid_apply_log_event::do_apply_event_worker

if(w->id==0)

{

std::cout<

sleep(20);

}

//pop_jobs_item

if(worker->id==0)

sleep(20);

开启slave以后,观察show full processlist和输出日志,在其中一个worker出现wait for preceding transaction to commit以后,执行 ftwrl,出现图1的死锁;wait for preceding transaction to commit以后,出现日志before commit之后,执行 ftwrl,出现图2的死锁。

如何解决?

出现死锁以后如果不人工干预,IO线程正常,但是SQL线程一直卡住,一般需要等待lock-wait-timeout时间,这个值我们线上设置1800秒,所以这个死锁会产生很大影响。

那么如何解决呢?kill !kill哪个线程呢?

对图1的死锁,146处于wait for preceding transaction状态的worker线程实际处于mysql_cond_wait的状态,kill不起作用,所以只能kill 145线程或者备份线程,如果kill145worker线程,整个并行复制就报错结束,show slave status显示SQL异常退出,之后需要手动重新开启sql线程,所以最好的办法就是kill执行flush tables with read lock的线程,代价最小。

至于图2的死锁,则只能kill掉执行flush tables with read lock的线程。所以出现上述死锁时,kill执行flush tables with read lock的备份线程就恢复正常,之后择机重新执行备份即可。

如何避免?

设置xtrabackup的kill-long-queries-timeout参数可以避免第一种死锁的出现,其实不算避免,只是出现以后xtrabackup会杀掉阻塞的执行语句的线程;但是这个参数对第二种死锁状态则无能为力了,因为xtrabackup选择杀掉的线程时,会过滤Info!=NULL。

另外还有个参数safe-slave-backup,执行备份的时候加上这个参数会停掉SQL线程,这样也肯定不会出现这个死锁,只是停掉SQL未免太暴力了,个人不提倡这样做。

可以设置slave_preserve_commit_order=0关闭从库binlog的顺序提交,关闭这个参数只是影响并行复制的事务在从库的提交顺序,对最终的数据一致性并无影响,所以如果无特别要求从库的binlog顺序必须与主库保持一致,可以设置slave_preserve_commit_order=0避免这个死锁的出现。

关于xtrabackup kill-long-query-type参数

首先说下```kill-long-queries-timeout,kill-long-query-type```参数,文档介绍如下

--KILL-LONG-QUERY-TYPE=ALL|SELECT

This option specifies which types of queries should be killed to

unblock the global lock. Default is “all”.

--KILL-LONG-QUERIES-TIMEOUT=SECONDS**

This option specifies the number of seconds innobackupex waits

between starting FLUSH TABLES WITH READ LOCK and killing those queries

that block it. Default is 0 seconds, which means innobackupex will not

attempt to kill any queries. In order to use this option xtrabackup

user should have PROCESS and SUPER privileges.Where supported (Percona

Server 5.6+) xtrabackup will automatically use Backup Locks as a

lightweight alternative to FLUSH TABLES WITH READ LOCK to copy non-

InnoDB data to avoid blocking DML queries that modify InnoDB tables.

参数的作用的就是在Xtrabackup执行FLUSH TABLES WITH READ LOCK以后,获得全局读锁时,如果有正在执行的事务会阻塞等待,kill-long-queries-timeout参数不为0时,xtrabackup内部创建一个线程,连接到数据库执行show full processlist,如果TIME超过kill-long-queries-timeout,会kill掉线程,kill-long-query-type设置可以kill掉的SQL类型。

官方文档介绍kill-long-query-type默认值时all,也就是所有语句都会kill掉。但在使用中发现,只设置kill-long-queries-timeout,未设置kill-long-query-type时,参数没起作用!最后查阅xtrabackup代码,如下:

{"kill-long-query-type", OPT_KILL_LONG_QUERY_TYPE,

"This option specifies which types of queries should be killed to "

"unblock the global lock. Default is \"all\".",

(uchar*) &opt_ibx_kill_long_query_type,

(uchar*) &opt_ibx_kill_long_query_type, &query_type_typelib,

GET_ENUM, REQUIRED_ARG, QUERY_TYPE_SELECT, 0, 0, 0, 0, 0}

心中一万头草泥马,也许只是笔误,但也太坑爹了!所以使用kill-long-query-type时一定要自己指定好类型!

总结

mysql死锁释放时间参数_由FTWRL导致的MySQL从库死锁分析及参数深究相关推荐

  1. mysql 设置电脑时间设置_怎样设置mysql密码

    有时我们会因为设置原因或时间长了忘记了数据库管理员的密码,使得我们被关在MySQL服务器外.MySQL服务器提供了一种方法可使我们在服务器上重设密码.在windows和linux/unix平台上操作稍 ...

  2. mysql 日期和时间函数_介绍一下mysql的日期和时间函数

    介绍一下mysql的日期和时间函数 mysql> SELECT PERIOD_DIFF(9802,199703); -> 11 DATE_ADD(date,INTERVAL expr ty ...

  3. mysql和linux的题目_最强Linux和Mysql面试题套餐,让你的面试无懈可击!

    引言: 大家好,我是一菲,在软件测试当中linux 操作系统和Mysql数据库的内容是十分的知识同时也是十分重要的.所以一菲这两天通过查阅资料等其他方式为大家梳理了liunx和Mysql面试题大礼包, ...

  4. MySQL常见的主从复制架构_如何搭建经典的MySQL 主从复制架构

    什么是MySQL主从架构 首先,大家来看一张图 master-slave 从上图中,可以看出,MySQL主从架构利用的是MySQL的主从复制原理,它主要分三个过程 1.master 主机将操作记录到二 ...

  5. mysql killed进程不结束_优秀的数据库产品——MySQL 云数据库服务

    作为一种低成本,高性能,高可靠性和开放源代码的数据库产品,MySQL已在Internet公司中广泛使用. 例如,淘宝有数千个MySQL服务器. 尽管NoSQL在过去两年中发展迅速,新产品层出不穷,但N ...

  6. mysql 5.5.安装包_完美!阿里内部MySQL笔记爆火,肝完不再删库到跑路!(文档+视频贼全!)...

    MySQL凭借着它还不错的性能.还不错的稳定性常年稳居老二宝座,当然最大的优势就是它不要钱,还开源,这让它成为大部分中小型公司,尤其是互联网公司首选的数据库(近年来越来越多的大公司也在尝试将业务转移到 ...

  7. mysql 8.0 集群_集群架构03·MySQL初识,mysql8.0环境安装,mysql多实例

    官方网址 https://dev.mysql.com/downloads/mysql/ 社区版本分析 MySQL5.5:默认存储引擎改为InnoDB,提高性能和可扩展性,增加半同步复制 MySQL5. ...

  8. mysql 人名用什么类型_如何选择合适的MySQL数据类型

    一.MySQL数据类型选择原则 更小的通常更好:一般情况下选择可以正确存储数据的最小数据类型.越小的数据类型通常更快,占用磁盘,内存和CPU缓存更小. 简单就好:简单的数据类型的操作通常需要更少的CP ...

  9. python 不定参数_人生苦短,我学不会Python。——函数中不定长参数的写法

    定义函数 我们可以定义一个实现自己想要的功能的函数.Python定义函数以def开头,定义函数基本格式如下: def 函数名 (参数列表):"函数_文档字符串"函数体 return ...

最新文章

  1. Java代码规范、格式化和checkstyle检查配置文档
  2. 高德地图定位精度多少米_中美俄卫星定位精度分别是多少?美0.1米,俄10米,中国呢?...
  3. 分区表分区字段的update操作
  4. 微信对账单 java_微信支付对账,你是如何处理的?
  5. 非线性最小二乘通俗易懂解释
  6. 游戏版号重新发放,开发者可以松口气了!| 畅言
  7. python100例详解-几个小例子给你讲解Python中类的描述符
  8. SQL Server树型结构数据处理的存储过程
  9. python bs4 基本应用
  10. 计算机办公自动化取证,办公自动化课程总结范文
  11. 全网最全移动端App性能测试方法,值得收藏!
  12. 《程序员修炼之道--从小工到专家》读书笔记
  13. fooview辅助功能 shell_Fooview:一款功能超多的桌面悬浮球,帮你摆脱多余操作
  14. JZOJ 1775. 合并果子2 (Standard IO)
  15. A027_MySQL进阶
  16. AutoDock, AutoDock-vina等对接工具安装
  17. 【TARS】分布式存储系统DCache
  18. 微信公众号回复、接收消息中中文乱码问题的分析及解决
  19. MATLAB解线性方程组和一元多次方程
  20. 电脑备忘录中怎么保存表格文件?

热门文章

  1. LiveVideoStack 主编观察 01
  2. 在3kbps的带宽下还能清晰地语音聊天?
  3. 音视频技术开发周刊 | 133
  4. Go基础编程:格式化输出、类型转换、类型别名
  5. FATAL: NO bootable medium found! System halted
  6. RxJava 2.0的基本使用
  7. Scala _03方法与函数
  8. SpringCloud 02_什么是分布式、多线程、高并发?(浅析)
  9. springboot环境搭建及入门必知
  10. leetcode 235. Lowest Common Ancestor of a Binary Search Tree | 235. 二叉搜索树的最近公共祖先(哈希表)