MySQL · 特性分析 · 到底是谁执行了FTWL

什么是FTWL

FTWRL是FLUSH TABLES WITH READ LOCK的简称(FTWRL),该命令主要用于保证备份一致性备份。为了达到这个目的,它需要关闭所有表对象,因此这个命令的杀伤性很大,执行命令时容易导致库hang住。如果它在主库执行,则业务无法正常访问;如果在备库,则会导致SQL线程卡住,主备延迟。 FTWRL通过持有以下两把全局的MDL(MetaDataLock)锁:

  • 全局读锁(lock_global_read_lock) 会导致所有的更新操作被堵塞
  • 全局COMMIT锁(make_global_read_lock_block_commit) 会导致所有的活跃事务无法提交

FLUSH TABLES WITH READ LOCK执行后整个系统会一直处于只读状态,直到显示执行UNLOCK TABLES。这点请切记。

如何高效定位FTWL的执行会话

由于FTWL持有的是MDL锁,所以一旦它执行完成,你将无法以定位DML锁的方式来定位它。即在show processlist的结果和information_schema相关的表中找不到任何相关的线索。我们来看下面的一个例子:

[test]> flush tables with read lock;
Query OK, 0 rows affected (0.06 sec)

[test]> show full processlist\G
*************************** 1. row ***************************
      Id: 10
    User: root
    Host: localhost
      db: test
 Command: Query
    Time: 0
   State: init
    Info: show full processlist
Progress: 0.000
*************************** 2. row ***************************
      Id: 11
    User: root
    Host: localhost
      db: test
 Command: Query
    Time: 743
   State: Waiting for global read lock
    Info: delete from t0
Progress: 0.000
2 rows in set (0.00 sec)

[test]> select * from information_schema.processlist\G
*************************** 1. row ***************************
           ID: 11
         USER: root
         HOST: localhost
           DB: test
      COMMAND: Query
         TIME: 954
        STATE: Waiting for global read lock
         INFO: delete from t0
      TIME_MS: 954627.587
        STAGE: 0
    MAX_STAGE: 0
     PROGRESS: 0.000
  MEMORY_USED: 67464
EXAMINED_ROWS: 0
     QUERY_ID: 1457
  INFO_BINARY: delete from t0
          TID: 8838
*************************** 2. row ***************************
           ID: 10
         USER: root
         HOST: localhost
           DB: test
      COMMAND: Query
         TIME: 0
        STATE: Filling schema table
         INFO: select * from information_schema.processlist
      TIME_MS: 0.805
        STAGE: 0
    MAX_STAGE: 0
     PROGRESS: 0.000
  MEMORY_USED: 84576
EXAMINED_ROWS: 0
     QUERY_ID: 1461
  INFO_BINARY: select * from information_schema.processlist
          TID: 8424
2 rows in set (0.02 sec)

从上的输出中,我们只发现了会话11 在等候一个全局读锁。但这个锁被谁持有,从这个输出里面我们找不到任何线索。我现在再来看看INNODB STATUS输出:

...
------------
TRANSACTIONS
------------
Trx id counter 20439
Purge done for trx's n:o < 20422 undo n:o < 0 state: running but idle
History list length 176
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started
MySQL thread id 11, OS thread handle 0x7f7f5cdb8b00, query id 1457 localhost root Waiting for global read lock
delete from t0
---TRANSACTION 0, not started
MySQL thread id 10, OS thread handle 0x7f7f5ce02b00, query id 1462 localhost root init
show engine innodb status
--------
...

我们从引擎层也没有找到相关的线索。这个毫无疑问,在本文开始的时候就已经指出了FTWL持有的事MDL锁。
当然因为这个例子中只有两个会话,你一眼就可以看出来谁持有了全局读锁。如果是线上的环境,将会有成百上千个会话。那又怎么办呢?请继续往下看。那我们如何快速定位FTWL的锁呢?主要有下面三种方法:

  • 如果你用的Mysql 5.7,那么你可以使用performance_schema.metadata_locks
  • 如果你用的Mysql 5.6,那么你可以使用performance_schema.events_statements_history
  • 如果你用的Mysql版本比较老,那么可以使用genearal log或者一些sql审计的日志来定位

以上三种方法都是要开启的,默认情况这些方法是没有开启的。所以在工作中,我们会经常遇到这种情况。
整个库都被堵住了。数据库里出现了大量的Waiting for global read lock等待。但上面提到的三种方法又不适用于我们。所以接下来我会为大家用展示一种利用gdb去快速定位执行FTWL的会话。我们来看下面的例子:

会话1:

flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)

会话2:
mysql> delete from t;  --被hang住

会话3:
mysql> show processlist;
+----+------+-----------+------+---------+------+------------------------------+------------------+
| Id | User | Host      | db   | Command | Time | State                        | Info             |
+----+------+-----------+------+---------+------+------------------------------+------------------+
|  7 | root | localhost | test | Query   |  227 | Waiting for global read lock | delete from t    |
|  8 | root | localhost | NULL | Sleep   |  215 |                              | NULL             |
|  9 | root | localhost | NULL | Query   |    0 | init                         | show processlist |
+----+------+-----------+------+---------+------+------------------------------+------------------+

由于会话1执行了FTWL,导致了会话2中的DML无法执行。接下来,我们演示如何通过gdb去定位执行了FTWL的会话。见下面的步骤

  1. 找出myql的进程id, ps -ef grep mysql

root 7743 2366 0 05:07 ? 00:00:01 /u02/mysql/bin/mysqld

2.利用gdb来跟踪mysql进程 执行 gdb -p 7743

3.在mysql把已经连接的会话保存在一个叫global_thread_list的全局变量中在这个变量中的thread有一个叫global_read_lock的变量来表示持有锁的情况。所以我们只有在gdb中找global_read_lock不为空的thread即可。所以我们在gdb中执行下面的语句

(gdb) pset global_thread_list THD*
elem[0]: $1 = (THD *) 0x4a55de0
elem[1]: $2 = (THD *) 0x4a5cf10
elem[2]: $3 = (THD *) 0x4b24aa0
Set size = 3

上面的命令输出了三个会话的内存地址。接下来我们根据这些内存地址去查找每个会话各自对应的global_read_lock

4.依次在dgb中打印上面三个会话中的global_read_lock和thread_id的值

(gdb) p ((THD *) 0x4a55de0)->global_read_lock
$4 = {
  static m_active_requests = 1,
  m_state = Global_read_lock::GRL_NONE,
  m_mdl_global_shared_lock = 0x0,
  m_mdl_blocks_commits_lock = 0x0
}   //这个会话的Global_read_lock为空,不是我们要找的

(gdb) p ((THD *) 0x4a5cf10)->global_read_lock
$5 = {
  static m_active_requests = 1,
  m_state = Global_read_lock::GRL_NONE,
  m_mdl_global_shared_lock = 0x0,
  m_mdl_blocks_commits_lock = 0x0
}   //这个会话的Global_read_lock也为空,不是我们要找的

(gdb) p ((THD *) 0x4b24aa0)->global_read_lock
$6 = {
  static m_active_requests = 1,
  m_state = Global_read_lock::GRL_ACQUIRED_AND_BLOCKS_COMMIT,
  m_mdl_global_shared_lock = 0x7f6034002bb0,
  m_mdl_blocks_commits_lock = 0x7f6034002c20
}
//这个会话的Global_read_lock不为空,GRL_ACQUIRED_AND_BLOCKS_COMMIT表示全局读锁与commit锁,这个就是我们要好的。我接下来打印出它的thread_id
p ((THD *) 0x4b24aa0)->thread_id
$7 = 8 //8号会话执行了FTWL

5.我们可以通过执行kill 8结束这个会话来释放全局的锁。让被堵住的会话,继续运行下去。

在新开的mysql会话中,执行下面的语句

mysql> kill 8

以前被堵在的会话中,会看到下面的结果
mysql> delete from t;
Query OK, 0 rows affected (40 min 20.73 sec)

小结

由于FTWL持有的是MetaDataLock类型的锁,所以给我们定位问题的源头带来很大的困难。很多同学在解决类似的问题的时候,会把运行时间最长的几个会话杀掉。这种方法并不可取。因为造成拥堵的源头并没有找到。所以我给大家提供了一个利用调试工具抓取mysql内部状态变量的方法来定位这类问题的源头。希望大家喜欢。

时间: 2025-01-05 01:17:40

MySQL · 特性分析 · 到底是谁执行了FTWL的相关文章

MySQL · 特性分析 · MDL 实现分析

前言 在MySQL中,DDL是不属于事务范畴的,如果事务和DDL并行执行,操作相关联的表的话,会出现各种意想不到问题,如事务特性被破坏.binlog顺序错乱等,为了解决类似这些问题,MySQL在5.5.3引入了MDL锁(Metadata Locking),关于其设计思路可以参考这两个worklog:WL#3726 和 WL#4284.本篇从代码实现角度对MDL进行分析. 重要数据结构 MDL 是在 MySQL server 层实现的一个模块,通过对外接口和server层其它模块进行交互,在sql

MySQL · 特性分析 · MySQL 5.7 外部XA Replication实现及缺陷分析

MySQL 5.7 外部XA Replication实现及缺陷分析 MySQL 5.7增强了分布式事务的支持,解决了之前客户端退出或者服务器关闭后prepared的事务回滚和服务器宕机后binlog丢失的情况. 为了解决之前的问题,MySQL5.7将外部XA在binlog中的记录分成了两部分,使用两个GTID来记录.执行prepare的时候就记录一次binlog,执行commit/rollback再记录一次.由于XA是分成两部分记录,那么XA事务在binlog中就可能是交叉出现的.Slave端的

MySQL · 特性分析 · 浅谈 MySQL 5.7 XA 事务改进

关于MySQL XA 事务 MySQL XA 事务通常用于分布式事务处理当中.比如在分库分表的场景下,当遇到一个用户事务跨了多个分区,需要使用XA事务 来完成整个事务的正确的提交和回滚,即保证全局事务的一致性. XA 事务在分库分表场景的使用 下图是个典型的分库分表场景,前端是一个Proxy后面带若干个MySQL实例,每个实例是一个分区. 假设一个表test定义如下,Proxy根据主键"a"算Hash决定一条记录应该分布在哪个节点上: create table test(a int p

MySQL · 特性分析 · 企业版特性一览

背景 MySQL 企业版由 Oracle 公司维护,当然也是收费的.其产品类别也基本和 Oracle 数据库一致,包括标准版.企业版.集群版等.标准版包括基本的特性,价格也会比企业版便宜很多.今天和小编一起来看下 MySQL Enterprise Edition 提供的一些功能,这些功能的源码当然是不开源的,也是企业版的卖点. 企业级备份恢复 备份 备份工具提供 InnoDB 的联机在线备份,同时 MyISAM 引擎的备份会阻塞写入.联机备份是否阻塞应用,还要根据引擎的特性来定.这点上,Perc

MySQL · 特性分析 · Index Condition Pushdown (ICP)

前言 上一篇文章 提过,我们在之后的文章中会从 optimizer 的选项出发,系统的介绍 optimizer 的各个变量,包括变量的原理.作用以及源码实现等,然后再进一步的介绍优化器的工作过程(SQL 语句扁平化处理.索引选择.代价计算.多表连接顺序选择以及物理执行等内容),本期我们先看一下众所周知的 ICP,官方文档请参考这里. ICP 测试 首先,咱们来看一下打开 ICP 与关闭 ICP 之间的性能区别,以下是测试过程: 准备数据: create table icp(id int, age

MySQL · 特性分析 ·MySQL 5.7新特性系列四

继上三期月报:MySQL 5.7新特性之一介绍了一些新特性及兼容性问题MySQL 5.7新特性之二介绍了临时表的优化和实现MySQL 5.7新特性之三介绍了undo表空间的truncate功能 这期我们一起来学习下MySQL 5.7的并行复制. 1. 背景 MySQL的master<->slave的部署结构,使用binlog日志保持数据的同步,全局有序的binlog在备库按照提交顺序进行回放. 由于新硬件的发展,SSD的引入和多core的CPU,master节点的并发处理能力持续提升,slav

MySQL · 特性分析 · 优化器 MRR &amp; BKA

上一篇文章咱们对 ICP 进行了一次全面的分析,本篇文章小编继续为大家分析优化器的另外两个选项: MRR & batched_key_access(BKA) ,分析一下他们的作用.原理.相互关系.源码实现以及使用范围. 什么是 MRR MRR 的全称是 Multi-Range Read Optimization,是优化器将随机 IO 转化为顺序 IO 以降低查询过程中 IO 开销的一种手段,咱们对比一下 mrr=on & mrr=off 时的执行计划: 其中表结构如下: mysql>

MySQL · 特性分析 ·MySQL 5.7新特性系列三

继上两期月报,MySQL5.7新特性之一介绍了一些新特性及兼容性问题,MySQL 5.7新特性之二介绍了临时表的优化和实现. 这期我们一起来学习下undo空间管理,重点介绍truncate功能. 1. 背景 InnoDB存储引擎中,undo在完成事务回滚和MVCC之后,就可以purge掉了,但undo在事务执行过程中,进行的空间分配如何回收,就变成了一个问题. 我们亲历用户的小实例,因为一个大事务,导致ibdata file到800G大小. 我们先大致看下InnoDB的undo在不同的版本上的一

MySQL · 特性分析 · InnoDB transaction history

1. 背景 在写压力负载比较重的MySQL实例上, InnoDB可能积累了较长的没有被purge掉的transaction history,导致实例性能的衰减,或者空闲空间被耗尽,下面就来看看它是怎么产生的,或者有没有什么方法来减轻,避免这样的问题出现. 2. InnoDB purge概要 InnoDB是一个事务引擎,实现了MVCC特性,也就是在存储引擎里对行数据保存了多个版本.在对行数据进行delete或者update更改时,行数据的前映像会保留一段时间,直到可以被删除的时候. 在大部分OLT