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

本文介绍了在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版的只读节点上存在未结束的查询或未提交的事务。

解决方案

  1. PolarDB MySQL版新增抢占式DDL来解决当前问题,您可以根据文档中的内容,来了解并开启当前功能。

  2. 提交事务(commit)或回滚事务(rollback)。

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

  3. 执行DDL操作。

    • 如果操作成功,任务结束。

    • 如果操作失败,继续下一步。

  4. 如果您集群中的Polar Performance Schema功能是开启状态,您可以利用Performance Schema查询目标表上MDL锁状态,然后来kill掉只读节点上未提交事务的相关线程。具体操作如下:

    说明
    • 您可以执行以下SQL查看集群中的Polar Performance Schema功能是否开启。

      SHOW VARIABLES LIKE 'polar_performance_schema';
    • 如果当前只读节点上未提交的事务非常重要,建议不要直接kill掉,请耐心等待此事务结束后,再进行DDL操作。

    • 建议您开启Polar Performance Schema功能。关于如何开启Polar Performance Schema功能的开启状态,请参见操作步骤

    • 如果kill不掉线程、线程ID不存在或者出现如下报错,请提交工单联系阿里云技术支持进行处理。

      ERROR 1094 (HY000): Unknown thread id: xxx

    使用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;

    显示结果如下:

    image

    从上图可以看到,当前test/t这个表上,有一个大查询或者未提交的事务,持有了LOCK_TYPESHARED_READ的锁。同时,当前test/t表上有一个LOCK_TYPEEXCLUSIVE的锁处于PENGING状态。使用Hint语法指定只读节点执行/*force_node='pi-bp10k7631d6k3****'*/ kill 14结束掉连接对应的线程即可。

  5. 如果您集群中的Polar Performance Schema功能是关闭状态,您可以查询information_schema.innodb_trx表上MDL锁状态,来确定是否有事务未提交,然后来kill掉对应的线程。具体操作如下:

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

    /*force_node='pi-bp10k7631d6k3****'*/ SELECT * FROM information_schema.innodb_trx\G

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

    从上图可以看到,当前表t1上面存在一个大查询,说明当前的连接持有了表t1的MDL锁。此时,使用Hint语法指定只读节点执行/*force_node='pi-bp10k7631d6k3****'*/ kill 6结束掉连接对应的trx_mysql_thread_id线程即可。

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

    image

    从上图可以看到,有一个未提交的事务14,由于trx_query字段为空,无法准确判断当前事务持有了当前表的MDL锁。此时您可以根据trx_started字段进行处理,如果trx_started字段的时间和当前时间的差距很大,大概率是当前事务14持有了MDL锁。此时,使用Hint语法指定只读节点执行/*force_node='pi-bp10k7631d6k3****'*/ kill 14结束掉连接对应的trx_mysql_thread_id线程即可。

联系我们

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

更多信息

云原生数据库PolarDB中DDL操作的过程如下:

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

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

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

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

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

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

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