MySQL8.0锁等待排查的实现

 更新时间:2024年09月09日 11:05:07   作者:Bing@DBA  
MySQL8.0相较于5.7版本在锁等待排查方面发生了显著变化,原有的INNODB_LOCKS和INNODB_LOCK_WAITS表被移除,本篇文章将结合这些改动,介绍 MySQL 8.0 版本如何排查锁等待问题,感兴趣的可以了解一下

前言

MySQL 5.7 版本的时候锁等待排查用的元数据,主要存储在 information_schema 库下的 INNODB_LOCKS 和 INNODB_LOCK_WAITS 表,8.0 版本这两张表删除了,在 performance_schema 提供新的锁相关的表,本篇文章将结合这些改动,介绍 MySQL 8.0 版本如何排查锁等待问题。

1. data_locks

performance_schema 库中的 data_locks 可以观测 MySQL 中的锁,对于 InnoDB 引擎可以观测到表锁、行锁、Gap 锁、Next-key 锁。值得注意的是 data_locks 表无论锁是否处理等待状态,都会记录,所以有利于用户通过该表测试 MySQL 的加锁逻辑。

  • ENGINE:持有锁的存储引擎。
  • ENGINE_LOCK_ID:内部格式,用户可忽略。
  • ENGINE_TRANSACTION_ID:事务 ID 可以与 INFORMATION_SCHEMA INNODB_TRX 表的 trx_id 字段关联起来。
  • THREAD_ID:创建锁的线程 ID,一般用不着,通过事务 ID 就可以定位到会话连接。
  • EVENT_ID:与 THREAD_ID 组合使用,可以从 events 表中查到 SQL 语句。
  • OBJECT_SCHEMA:锁定的数据库名称。
  • OBJECT_NAME:锁定表的名称。
  • PARTITION_NAME:锁定分区的名称,如果不是分区表为 NULL。
  • SUBPARTITION_NAME:锁定子分区的名称,如果不是分区表为 NULL。
  • INDEX_NAME:索引的名称。
  • OBJECT_INSTANCE_BEGIN:锁在内存中的地址。
  • LOCK_TYPE:锁的类型,对于 InnoDB,允许的值是 RECORD 行级锁, TABLE 对于表级锁。
  • LOCK_MODE:锁的行为,用来标记是意向锁、写锁、读锁、间隙锁、Next-key 锁。
  • LOCK_STATUS:锁请求的状态,对于 InnoDB 引擎有 GRANTED 已持有和 WAITING 正在等待锁,两种状态。
  • LOCK_DATA:如果是在主键加锁,显示主键值,如果是二级索引加锁显示二级索引的值和对应主键的值。

下面的 SQL 是精简过的,只保留了常用的字段:

select ENGINE_TRANSACTION_ID, OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_STATUS, LOCK_DATA from performance_schema.data_locks;

Session 1:锁定 test_semi 全表。

begin;
select * from test_semi for update;
+----+------+------+
| a  | b    | c    |
+----+------+------+
| 10 |    1 |  123 |
| 11 |    2 |  123 |
| 12 |    1 |  123 |
| 13 |    2 |  123 |
| 14 |    1 |  123 |
+----+------+------+

Session 2:由下表可以看出 test_semi 表中每行都在主键上加写锁,在 test_semi 表上加入 IX 意向锁。

ENGINE_TRANSACTION_IDOBJECT_SCHEMAOBJECT_NAMEINDEX_NAMELOCK_TYPELOCK_MODELOCK_STATUSLOCK_DATA
6711testtest_semiNULLTABLEIXGRANTEDNULL
6711testtest_semiPRIMARYRECORDX,REC_NOT_GAPGRANTED10
6711testtest_semiPRIMARYRECORDX,REC_NOT_GAPGRANTED11
6711testtest_semiPRIMARYRECORDX,REC_NOT_GAPGRANTED12
6711testtest_semiPRIMARYRECORDX,REC_NOT_GAPGRANTED13
6711testtest_semiPRIMARYRECORDX,REC_NOT_GAPGRANTED14

2. data_lock_waits

performance_schema 库中的 data_lock_waits 表可以观测锁等待的情况,只有发生堵塞的时候才会记录。如果你发现这张表的记录很多,说明目前数据库有很多锁等待的情况。

  • ENGINE:存储引擎。
  • REQUESTING_ENGINE_LOCK_ID:存储引擎请求的锁的 ID。
  • REQUESTING_ENGINE_TRANSACTION_ID:被堵塞的事务 ID 可以与 INFORMATION_SCHEMA INNODB_TRX 表的 trx_id 字段关联起来。
  • REQUESTING_THREAD_ID:请求锁的会话的线程 ID。
  • REQUESTING_EVENT_ID:在请求锁的会话中导致锁请求的性能模式事件。
  • REQUESTING_OBJECT_INSTANCE_BEGIN:请求的锁在内存中的地址。
  • BLOCKING_ENGINE_LOCK_ID:阻塞锁的 ID,可以与 data_locks 表的 ENGINE_LOCK_ID 字段进行关联。
  • BLOCKING_ENGINE_TRANSACTION_ID:持有锁的事务 ID 可以与 INFORMATION_SCHEMA INNODB_TRX 表的 trx_id 字段关联起来。
  • BLOCKING_THREAD_ID:持有阻塞锁的会话的线程 ID。
  • BLOCKING_EVENT_ID:导致持有该锁的会话中出现阻塞锁的性能模式事件。
  • BLOCKING_OBJECT_INSTANCE_BEGIN:阻塞锁在内存中的地址。

基于该表与事务表的关联可以获得当前堵塞的事务信息:

select 
   trx.trx_id as waiting_trx_id,
   trx.trx_mysql_thread_id as waiting_thread_id,
   trx.trx_state as waiting_trx_state,
   trx.trx_query as waiting_query,
   lk.BLOCKING_ENGINE_TRANSACTION_ID as blocking_trx_id,
   lk.BLOCKING_THREAD_ID as blocking_thread_id,
   trx.trx_wait_started as trx_wait_started,
   TIMESTAMPDIFF(SECOND, trx.trx_wait_started, CURRENT_TIMESTAMP) as wait_second
from 
  performance_schema.data_lock_waits as lk 
  join information_schema.INNODB_TRX as trx on lk.REQUESTING_ENGINE_TRANSACTION_ID = trx.trx_id;
  • waiting_trx_id:被堵塞的事务 ID。
  • waiting_thread_id:被堵塞的线程 ID。
  • waiting_trx_state:被堵塞事务的状态。
  • waiting_query:被堵塞事务的语句。
  • blocking_trx_id:堵塞该事务的事务 ID。
  • blocking_thread_id:堵塞该事务的线程 ID,如果查询返回很多行,且大部分该值都相同,说明堵塞源都相同,可通过该 ID 查到会话 ID 并 kill 掉。
  • trx_wait_started:被堵塞事务的开始时间。
  • wait_second:锁堵塞的时间长,单位为秒。

3. sys.innodb_lock_waits

sys 库里面大部分表都是视图,MySQL 创建该库的原因是为了简化 performance_schema 表的使用难度,该库里面提供一个视图,可以查到非常详细的锁堵塞信息。

*************************** 1. row ***************************
                wait_started: 2024-08-06 15:37:36
                    wait_age: 00:00:11
               wait_age_secs: 11
                locked_table: `test`.`test_semi`
         locked_table_schema: test
           locked_table_name: test_semi
      locked_table_partition: NULL
   locked_table_subpartition: NULL
                locked_index: PRIMARY
                 locked_type: RECORD
              waiting_trx_id: 421847145074688
         waiting_trx_started: 2024-08-06 15:37:36
             waiting_trx_age: 00:00:11
     waiting_trx_rows_locked: 1
   waiting_trx_rows_modified: 0
                 waiting_pid: 818473
               waiting_query: select * from test_semi for share
             waiting_lock_id: 140372168364032:10:4:2:140372080646752
           waiting_lock_mode: S,REC_NOT_GAP
             blocking_trx_id: 6711
                blocking_pid: 819104
              blocking_query: NULL
            blocking_lock_id: 140372168364840:10:4:2:140372080652768
          blocking_lock_mode: X,REC_NOT_GAP
        blocking_trx_started: 2024-08-06 14:35:20
            blocking_trx_age: 01:02:27
    blocking_trx_rows_locked: 5
  blocking_trx_rows_modified: 0
     sql_kill_blocking_query: KILL QUERY 819104
sql_kill_blocking_connection: KILL 819104

结果集合中还给出 kill 掉堵塞会话的 SQL,不过在云数据库上面 sys 库一般都没有给用户权限。

4. 状态变量

可通过下方状态变量了解数据库中的行锁信息:

  • Innodb_row_lock_current_waits:当前正在等待行锁的操作数。
  • Innodb_row_lock_time:获取行锁花费的总时间,单位毫秒。
  • Innodb_row_lock_time_avg:获取行锁花费的平均时间,单位毫秒。
  • Innodb_row_lock_time_max:获取行锁花费的最大时间,单位毫秒。

下面我们来做一个实验:

root@mysql 14:38:  [(none)]>show status like '%Innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 33165 |
| Innodb_row_lock_time_avg      | 16582 |
| Innodb_row_lock_time_max      | 28845 |
| Innodb_row_lock_waits         | 2     |
+-------------------------------+-------+
Session 1Session 2
Begin;
delete from score where id = 5;
update score set number = 66 where id = 5; – 等待行锁
root@mysql 14:41:  [test]>show status like '%Innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 1     |
| Innodb_row_lock_time          | 33165 |
| Innodb_row_lock_time_avg      | 11055 |
| Innodb_row_lock_time_max      | 28845 |
| Innodb_row_lock_waits         | 3     |
+-------------------------------+-------+

此时可以发现 Innodb_row_lock_waits 和 Innodb_row_lock_current_waits 都增长了,time 相关的变量需要等事务结束后才会进行计算。

5. 状态变量 bug

Innodb_row_lock_current_waits 从文档描述来看,反映的是当前数据库行锁的操作数,不过该值有时会出现不准的情况。有位研发问我某云的监控上显示当前数据库的行锁有 20 亿个,当前数据库还正常吗?当时吓了一跳,会话没有任何异常,而且使用刚才介绍的锁排查方法,都没有异常。最后发现监控采集的是 Innodb_row_lock_current_waits 的值,最后发现该值非常不准,有 Bug,所以大家如果遇到此类问题,可以先忽略,自制锁等待监控可以查 data_lock_waits 表,但是频次不建议太高。

Innodb_row_lock_current_waits Bug:https://bugs.mysql.com/bug.php?id=71520

总结

MySQL 5.7 一些锁监控表,在 8.0 都发生了变化,不过 sys 库的 innodb_lock_waits 两个版本都通用,其实该表就是一个视图,在两个版本中的实现方式不一样,作用都相同。

到此这篇关于MySQL8.0锁等待排查的实现的文章就介绍到这了,更多相关MySQL8.0锁等待排查内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • mysql的分区技术详细介绍

    mysql的分区技术详细介绍

    这篇文章主要介绍了mysql的分区技术详细介绍,本文讲解了分区技术概述、分区的类型及分区操作等内容,需要的朋友可以参考下
    2015-03-03
  • MySQL8.x使用GRANT为用户赋权时报错的解决

    MySQL8.x使用GRANT为用户赋权时报错的解决

    这篇文章主要介绍了MySQL8.x使用GRANT为用户赋权时报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • MySQL完整性约束的定义与实例教程

    MySQL完整性约束的定义与实例教程

    数据完整性约束是对关系性模型完整性规则做某种约束条件,这篇文章主要给大家介绍了关于MySQL完整性约束的相关资料,需要的朋友可以参考下
    2021-05-05
  • MySQL慢sql优化思路详细讲解

    MySQL慢sql优化思路详细讲解

    在日常开发工作中数据库是常用的数据存储组件,一旦使用了数据库,那慢查询SQL的优化是绕不开的一道坎,下面这篇文章主要给大家介绍了关于MySQL慢sql优化思路的相关资料,需要的朋友可以参考下
    2023-01-01
  • Windows下简单的Mysql备份BAT脚本分享

    Windows下简单的Mysql备份BAT脚本分享

    朋友说在windows下面用bat命令备份失败,他一时找不到问题所在,于是找我帮忙查看下。查找后解决了该问题,想着总结下来,所以这篇文章主要跟大家分享了一个在Windows下简单的Mysql备份BAT脚本,需要的朋友可以参考下。
    2017-03-03
  • 浅析mysql迁移到clickhouse的5种方法

    浅析mysql迁移到clickhouse的5种方法

    这篇文章主要介绍了mysql迁移到clickhouse的5种方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-07-07
  • Windows 64位重装MySQL的教程(Zip版、解压版MySQL安装)

    Windows 64位重装MySQL的教程(Zip版、解压版MySQL安装)

    这篇文章主要介绍了Windows 64位,重装MySQL的方法(Zip版、解压版MySQL安装),本文给大家介绍的非常详细,具有一定的参考借鉴价值需要的朋友可以参考下
    2020-02-02
  • 一文掌握MySQL锁表方法

    一文掌握MySQL锁表方法

    在MySQL中,可以使用多种方法来锁定表,这些方法取决于你的具体需求,比如是锁定整个表还是行级锁,本文给大家分享一些常用的MySQL锁表方法,感兴趣的朋友跟随小编一起看看吧
    2024-02-02
  • mysql 维护常用命令

    mysql 维护常用命令

    需要维护mysql数据库的朋友可以参考下。
    2009-11-11
  • 安装配置MySQLMTOP来监控MySQL运行性能的教程

    安装配置MySQLMTOP来监控MySQL运行性能的教程

    这篇文章主要介绍了安装配置MySQLMTOP来监控MySQL运行性能的教程,MySQLMTOP具有B/S方式的图形化操作页面,需要的朋友可以参考下
    2015-12-12

最新评论