执行DDL操作提示“获取不到MDL锁”

更新时间:
复制 MD 格式

本文介绍了在PolarDB MySQL中执行DDL操作时提示“获取不到MDL锁”的解决方法。

问题现象

PolarDB MySQL数据库中执行DDL操作时提示获取不到MDL锁,报错信息如下:

ERROR HY000: Fail to get MDL on replica during DDL synchronize
ERROR HY000: Fail to get table lock on replica; you can 'set polar_support_mdl_sync_preemption = ON' and try restarting transaction

问题原因

PolarDB MySQL的只读节点上存在未结束的查询或未提交的事务。

解决方案

您可以选择以下任一方案执行,以解决您目前所遇到的问题:

  • 建议您可以直接使用第一种方案。为只读节点开启抢占式DDL功能来解决当前问题。具体操作,请参见抢占式DDL

  • 提交(commit)回滚(rollback)只读节点上未提交的事务。

    在只读节点上开启polar_slave_work_on_nonblock_mdl_mode参数,防止只读节点上未提交的长事务阻塞DDL操作。具体操作,请参见防止只读节点上长事务阻塞DDL操作

  • 您可以登录PolarDB控制台,前往集群的诊断与优化 > 一键诊断页面,在会话管理页签中检查是否存在异常会话。若发现异常会话,请单击异常区域内的查看详情以获取异常会话的具体信息。此时,建议您结束这些长时间未提交的事务的会话。对于其他异常会话,请根据实际业务场景进行优化结束。更多信息,请参见会话管理

  • 您可以通过集群中的Polar Performance Schema功能,查询目标表的MDL锁状态,并终止只读节点上未提交事务的相关线程,以解决您的问题。具体操作步骤如下:

    1. 查看集群中Polar Performance Schema功能的状态。您可以通过以下两种方式来查看:

      • 您可以登录PolarDB控制台,前往集群的配置与管理 > 参数配置页面查看参数loose_polar_performance_schema的值。

        集群参数在控制台上都已加上MySQL配置文件的兼容性前缀loose_

        确认集群参数 loose_polar_performance_schema 的值为 ON

      • 执行以下SQL:

        SHOW VARIABLES LIKE 'polar_performance_schema';
        +--------------------------+-------+
        | Variable_name            | Value |
        +--------------------------+-------+
        | polar_performance_schema | ON    |
        +--------------------------+-------+

    2. 根据参数的开启与关闭状态,处理方式将有所不同。

      开启(ON)

      您可以通过performance_schema.metadata_locks来查询目标表上MDL锁状态,来确定是否有事务未提交。然后使用kill命令终止未提交事务的相关线程。

      1. 使用Hint语法指定只读节点执行以下SQL查询目标表上MDL锁状态。

        /*force_node='pi-bp10k7631d6k3****'*/ SELECT t.PROCESSLIST_ID, m.OBJECT_TYPE, m.OBJECT_SCHEMA, m.OBJECT_NAME, m.LOCK_TYPE, m.LOCK_DURATION, m.LOCK_STATUS FROM performance_schema.metadata_locks m LEFT JOIN performance_schema.threads t ON m.owner_thread_id=t.thread_id;

        显示结果如下:

        | PROCESSLIST_ID | OBJECT_TYPE | OBJECT_SCHEMA      | OBJECT_NAME    | LOCK_TYPE           | LOCK_DURATION | LOCK_STATUS |
        |           NULL | SCHEMA      | test01             | NULL           | INTENTION_EXCLUSIVE | EXPLICIT      | GRANTED     |
        |           NULL | TABLE       | test01             | t1             | EXCLUSIVE           | EXPLICIT      | PENDING     |
        |      268541021 | TABLE       | performance_schema | metadata_locks | SHARED_READ         | TRANSACTION   | GRANTED     |
        |      268541021 | TABLE       | performance_schema | threads        | SHARED_READ         | TRANSACTION   | GRANTED     |
        |      536976473 | TABLE       | test01             | t1             | SHARED_READ         | TRANSACTION   | GRANTED     |
        |      536976473 | SCHEMA      | test01             | NULL           | INTENTION_EXCLUSIVE | TRANSACTION   | GRANTED     |

        从以上查询结果可以看到,当前test01/t1这个表上,有一个大查询或者未提交的事务,持有了LOCK_TYPESHARED_READ的锁。同时,当前test/t1表上有一个LOCK_TYPEEXCLUSIVE的锁处于PENGING状态。

      2. 使用Hint语法指定只读节点执行kill命令终止连接对应的线程即可。

        说明
        • 如果当前只读节点上未提交的事务非常重要,建议不要直接使用kill命令终止当前事务,请耐心等待此事务结束后,再进行DDL操作。

        • 如果使用kill命令无法终止线程,导致线程ID不存在或者出现如下错误,请联系我们处理。

          ERROR 1094 (HY000): Unknown thread id: xxx
        /*force_node='pi-bp10k7631d6k3****'*/ kill 536976473;

      关闭(OFF)

      您可以通过information_schema.innodb_trx来查询目标表上MDL锁状态,来确定是否有事务未提交。然后使用kill命令终止未提交事务的相关线程。

      1. 使用Hint语法指定只读节点执行以下SQL查询information_schema.innodb_trx表上MDL锁状态。

        /*force_node='pi-bp10k7631d6k3****'*/ SELECT * FROM information_schema.innodb_trx\G
      2. 根据结果判断是大查询还是大事务导致的:

        • 大查询导致的DDL执行失败显示结果如下:

          mysql> SELECT * FROM information_schema.innodb_trx\G
          *************************** 1. row ***************************
                              trx_id: 421236312114112
                           trx_state: RUNNING
                         trx_started: 2025-01-03 17:35:24
               trx_requested_lock_id: NULL
                    trx_wait_started: NULL
                          trx_weight: 0
                 trx_mysql_thread_id: 536975732
                           trx_query: select sleep(100) from t1
                 trx_operation_state: NULL
                   trx_tables_in_use: 1
                   trx_tables_locked: 0
                    trx_lock_structs: 0
               trx_lock_memory_bytes: 1136
                     trx_rows_locked: 0
                   trx_rows_modified: 0
             trx_concurrency_tickets: 0
                 trx_isolation_level: READ COMMITTED
                   trx_unique_checks: 1
              trx_foreign_key_checks: 1
          trx_last_foreign_key_error: NULL
           trx_adaptive_hash_latched: 0
           trx_adaptive_hash_timeout: 0
                    trx_is_read_only: 1
          trx_autocommit_non_locking: 1
          1 row in set (0.00 sec)

          从以上查询结果可以看到,当前表t1上面存在一个大查询,说明当前的连接持有了表t1MDL锁。

        • 大事务导致的DDL执行失败显示结果如下:

          mysql> /*force_node='pi-bp10k7631d6k3*****'*/ SELECT * FROM information_schema.innodb_trx\G
          *************************** 1. row ***************************
                              trx_id: 33735
                           trx_state: RUNNING
                         trx_started: 2025-01-06 13:58:07
               trx_requested_lock_id: NULL
                    trx_wait_started: NULL
                          trx_weight: 2
                 trx_mysql_thread_id: 537247177
                           trx_query: NULL
                 trx_operation_state: NULL
                   trx_tables_in_use: 0
                   trx_tables_locked: 1
                    trx_lock_structs: 1
               trx_lock_memory_bytes: 1136
                     trx_rows_locked: 0
                   trx_rows_modified: 1
             trx_concurrency_tickets: 0
                 trx_isolation_level: READ COMMITTED
                   trx_unique_checks: 1
              trx_foreign_key_checks: 1
          trx_last_foreign_key_error: NULL
           trx_adaptive_hash_latched: 0
           trx_adaptive_hash_timeout: 0
                    trx_is_read_only: 0
          trx_autocommit_non_locking: 0
          1 row in set (0.00 sec)

          从以上查询结果可以看到,有一个未提交的事务537247177,由于trx_query字段为空,无法准确判断当前事务持有了当前表的MDL锁。此时您可以根据trx_started字段进行处理,如果trx_started字段的时间和当前时间的差距很大,大概率是当前事务537247177持有了MDL锁。

      3. 使用Hint语法指定只读节点执行kill命令终止连接对应的trx_mysql_thread_id线程即可。

        /*force_node='pi-bp10k7631d6k3****'*/ kill 537247177

联系我们

若您对DDL操作有任何疑问,可通过钉钉搜索群号入群咨询。您可以直接@群内专家,并附上您要咨询的问题;同时群内也有PolarDB MySQL小助手24*7小时在线回答您的问题。钉钉群号:15375044501。

更多信息

云原生数据库PolarDBDDL操作的过程如下:

  1. DDL操作的不同阶段,如果需要变更表结构,则在变更前,主节点先获取MDL锁,然后写入一条Redo日志。

  2. 只读节点解析到此Redo日志时,会尝试获取同一个表中的MDL锁。然后有下列两种情况:

    • 获取成功,进入下一步。

    • 获取失败,只读节点反馈给主节点。

  3. 主节点等待所有只读节点同步到最新的复制位点。在一定时间内,主节点会判断所有只读节点是否都解析到了此Redo日志以及是否加锁成功。然后有下列两种情况:

    • 所有只读节点同步到最新的复制位点,DDL成功。

    • 某些只读节点未同步到最新的复制位点,回滚DDL并报错。此时报错又分为两种情况:一种是等待同步超时的报错;另一种是本文中加锁失败的报错,而某些只读节点加锁失败的主要原因是只读节点存在未结束的查询或者未提交的事务。

常见问题

为什么使用kill命令无法终止指定会话?

当您执行kill命令时,系统会在会话上标记指定标签,并需等待系统内核层面完成终止操作。若指定的会话是一个大事务,则会话需在事务回滚完成后才能被终止。您可以通过查询information_schema.innodb_trx来确认会话是否处于回滚状态,具体查询语句为:

SELECT TRX_ID, TRX_STATE, TRX_STARTED, TRX_QUERY FROM information_schema.innodb_trx WHERE trx_mysql_thread_id = <会话ID>; 

当查询结果中TRX_STATEROLLING BACK时,需等待事务回滚完成,才能自动执行kill操作。