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

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

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

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

  1. ------------
  2. TRANSACTIONS
  3. ------------
  4. Trx id counter 20439
  5. Purge done for trx's n:o < 20422 undo n:o < 0 state: running but idle
  6. History list length 176
  7. LIST OF TRANSACTIONS FOR EACH SESSION:
  8. ---TRANSACTION 0, not started
  9. MySQL thread id 11, OS thread handle 0x7f7f5cdb8b00, query id 1457 localhost root Waiting for global read lock
  10. delete from t0
  11. ---TRANSACTION 0, not started
  12. MySQL thread id 10, OS thread handle 0x7f7f5ce02b00, query id 1462 localhost root init
  13. --------

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

  • 如果你用的Mysql 5.6,那么你可以使用performance_schema.events_statements_history

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

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

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

  1. 找出myql的进程id, ps -ef | grep mysql
  1. root 7743 2366 0 05:07 ? 00:00:01 /u02/mysql/bin/mysqld

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

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

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

  1. (gdb) p ((THD *) 0x4a55de0)->global_read_lock
  2. $4 = {
  3. static m_active_requests = 1,
  4. m_state = Global_read_lock::GRL_NONE,
  5. m_mdl_global_shared_lock = 0x0,
  6. m_mdl_blocks_commits_lock = 0x0
  7. } //这个会话的Global_read_lock为空,不是我们要找的
  8. (gdb) p ((THD *) 0x4a5cf10)->global_read_lock
  9. static m_active_requests = 1,
  10. m_state = Global_read_lock::GRL_NONE,
  11. m_mdl_blocks_commits_lock = 0x0
  12. } //这个会话的Global_read_lock也为空,不是我们要找的
  13. (gdb) p ((THD *) 0x4b24aa0)->global_read_lock
  14. $6 = {
  15. static m_active_requests = 1,
  16. m_state = Global_read_lock::GRL_ACQUIRED_AND_BLOCKS_COMMIT,
  17. m_mdl_global_shared_lock = 0x7f6034002bb0,
  18. m_mdl_blocks_commits_lock = 0x7f6034002c20
  19. }
  20. //这个会话的Global_read_lock不为空,GRL_ACQUIRED_AND_BLOCKS_COMMIT表示全局读锁与commit锁,这个就是我们要好的。我接下来打印出它的thread_id
  21. p ((THD *) 0x4b24aa0)->thread_id
  22. $7 = 8 //8号会话执行了FTWL

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