MySQL 5.7表上的独占锁和shared_read锁导致死锁/冻结

xfyts7mz  于 2023-05-05  发布在  Mysql
关注(0)|答案(1)|浏览(190)

我正在修改一个表以添加注解,列的类型和其他参数没有改变:

ALTER TABLE `table1` CHANGE `id` `id` INT(11) NOT NULL AUTO_INCREMENT
COMMENT 'test1';

当我尝试在具有中等流量的生产服务器上运行此更改时,我看到了类似死锁的场景。请求SHARED_READ锁的请求无法继续(它们是PENDING),并且EXCLUSIVE锁也是PENDING,因此系统冻结并且无法处理针对表的查询。
查看文档,MySQL应该能够在允许innodb表上的DML事务的同时进行DDR。我是不是漏掉了什么?
使用下面的查询,我可以看到锁:

SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID, PROCESSLIST_ID, PROCESSLIST_HOST, PROCESSLIST_STATE, PROCESSLIST_INFO, PROCESSLIST_TIME

FROM performance_schema.metadata_locks 
LEFT JOIN performance_schema.threads ON threads.THREAD_ID = metadata_locks.OWNER_THREAD_ID

order by owner_thread_id DESC;

这就是MySQL的工作原理吗?你不能在一个有中等流量的系统上功能性地改变一个表?

OBJECT_TYP OBJECT_SCHEMA     OBJECT_NAME   LOCK_TYPE           LOCK_DURATION LOCK_STATUS OWNER_THREAD_ID  PROCESSLIST_ID PROCESSLIST_HOST PROCESSLIST_STATE          PROCESSLIST_INFO                PROCESSLIST_TIME
TABLE      main_db           table2        SHARED_READ         TRANSACTION   GRANTED     80801101         80801075       IP2              Waiting for table metadata SELECT FROM table1              16
TABLE      main_db           table1        SHARED_READ         TRANSACTION   PENDING     80801101         80801075       IP2              Waiting for table metadata SELECT FROM table1              16
TABLE      main_db           table1        SHARED_READ         TRANSACTION   PENDING     80799419         80799393       IP1              Waiting for table metadata SELECT FROM table1 and table2   11
GLOBAL     NULL              NULL          INTENTION_EXCLUSIVE STATEMENT     GRANTED     80777210         80777184       IP1              Waiting for table metadata ALTER TABLE table1 add comment  17
SCHEMA     main_db           NULL          INTENTION_EXCLUSIVE TRANSACTION   GRANTED     80777210         80777184       IP1              Waiting for table metadata ALTER TABLE table1 add comment  17
TABLE      main_db           table1        SHARED_UPGRADABLE   TRANSACTION   GRANTED     80777210         80777184       IP1              Waiting for table metadata ALTER TABLE table1 add comment  17
TABLE      main_db           table1        EXCLUSIVE           TRANSACTION   PENDING     80777210         80777184       IP1              Waiting for table metadata ALTER TABLE table1 add comment  17

如果我让它停留30秒,它仍然不会前进到获取锁来更改表,并且会阻止选择。

rxztt3cl

rxztt3cl1#

你现在看到的并不叫死锁。死锁是指两个或多个事务处于相互依赖的锁等待循环中,因此无法中断等待。在这种情况下,除了终止其中一个事务之外,没有什么可做的。我不认为我见过MySQL中涉及元数据锁的死锁,只有行锁。
您看到的是MySQL元数据锁的工作方式。这些与InnoDB存储引擎中的行锁不同。
任何DDL语句,即使是只更改注解的DDL语句,也需要一个排他的元数据锁。
但是,任何阅读或写入该表的会话都会持有共享元数据锁,直到其事务结束。共享元数据锁阻止排他元数据锁请求。独占意味着没有其他会话可以持有任何元数据锁(共享的或以其他方式),因此元数据锁请求等待所有会话完成它们的事务并释放它们的共享元数据锁。
一旦DDL语句开始等待它的独占元数据锁,所有其他想要运行简单查询的会话都需要它们的共享元数据锁,而未完成的DDL锁请求会阻塞它们。所以他们也在等待。通过这种方式,单个DDL语句的等待可以阻止在DDL语句开始等待之后启动其请求的每个人。
为什么DDL语句在等待?因为在DDL语句启动时持有共享元数据锁的一个或多个会话不会给予它们的元数据锁。他们可能正在运行一个非常长的查询,但也有可能他们的查询已经完成,他们只是不会COMMIT或ROLLBACK来释放他们的锁。这取决于应用程序代码设计。
您应该对应用程序进行编码,以使事务尽可能短:

  • 优化单个查询,使它们不会运行太长时间。
  • 不要将太多不相关的查询组合到一个事务中。事务应该表示需要在同一事务中的有限查询组。我见过一些开发人员将无限数量的查询组合到一个长期存在的事务中,没有任何好的理由。
  • 在事务中一起执行所有查询,而不需要在它们之间执行不必要的非数据库代码,然后尽可能迅速地COMMIT或ROLLBACK。我曾经看到在一些应用程序中,事务意外地变得很长,因为在查询之间,应用程序向web服务或其他东西发出了一个http请求,这需要很长时间。同时,数据库事务在未提交状态下持续的时间过长。

元数据锁请求是否会像行锁请求一样超时?是的,有超时,但元数据锁请求的超时时间默认为1年(这是lock_wait_timeout配置变量的值)。
是否可以减少元数据锁请求超时?是的,你可以。在一个模式变更管理应用程序中,我们将lock_wait_timeout设置为2秒,因此如果应用程序很忙碌,至少DDL语句不会造成严重的阻塞(每个请求不超过2秒;如果其他会话需要运行在毫秒内完成的查询,则这可能有些糟糕)。
如果DDL语句超时,我们的服务将重试该语句。在短暂的睡眠间隔后,它将重试多达200次。如果它仍然失败(例如,如果它永远不会在由其他会话运行的长时间运行的事务之间滑动,那么它可能会失败),那么模式更改将失败并返回错误。这意味着必须在其他时间尝试改变,希望在交通较轻的时候。

相关问题