我正在修改一个表以添加注解,列的类型和其他参数没有改变:
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秒,它仍然不会前进到获取锁来更改表,并且会阻止选择。
1条答案
按热度按时间rxztt3cl1#
你现在看到的并不叫死锁。死锁是指两个或多个事务处于相互依赖的锁等待循环中,因此无法中断等待。在这种情况下,除了终止其中一个事务之外,没有什么可做的。我不认为我见过MySQL中涉及元数据锁的死锁,只有行锁。
您看到的是MySQL元数据锁的工作方式。这些与InnoDB存储引擎中的行锁不同。
任何DDL语句,即使是只更改注解的DDL语句,也需要一个排他的元数据锁。
但是,任何阅读或写入该表的会话都会持有共享元数据锁,直到其事务结束。共享元数据锁阻止排他元数据锁请求。独占意味着没有其他会话可以持有任何元数据锁(共享的或以其他方式),因此元数据锁请求等待所有会话完成它们的事务并释放它们的共享元数据锁。
一旦DDL语句开始等待它的独占元数据锁,所有其他想要运行简单查询的会话都需要它们的共享元数据锁,而未完成的DDL锁请求会阻塞它们。所以他们也在等待。通过这种方式,单个DDL语句的等待可以阻止在DDL语句开始等待之后启动其请求的每个人。
为什么DDL语句在等待?因为在DDL语句启动时持有共享元数据锁的一个或多个会话不会给予它们的元数据锁。他们可能正在运行一个非常长的查询,但也有可能他们的查询已经完成,他们只是不会COMMIT或ROLLBACK来释放他们的锁。这取决于应用程序代码设计。
您应该对应用程序进行编码,以使事务尽可能短:
元数据锁请求是否会像行锁请求一样超时?是的,有超时,但元数据锁请求的超时时间默认为1年(这是
lock_wait_timeout
配置变量的值)。是否可以减少元数据锁请求超时?是的,你可以。在一个模式变更管理应用程序中,我们将
lock_wait_timeout
设置为2秒,因此如果应用程序很忙碌,至少DDL语句不会造成严重的阻塞(每个请求不超过2秒;如果其他会话需要运行在毫秒内完成的查询,则这可能有些糟糕)。如果DDL语句超时,我们的服务将重试该语句。在短暂的睡眠间隔后,它将重试多达200次。如果它仍然失败(例如,如果它永远不会在由其他会话运行的长时间运行的事务之间滑动,那么它可能会失败),那么模式更改将失败并返回错误。这意味着必须在其他时间尝试改变,希望在交通较轻的时候。