我有一堆连接在事务中执行 SELECT,还有一个执行 DDL。mysql 手册非常清楚地说明了如何在事务中获取元数据锁:
为了确保事务可序列化性,服务器不得允许 会话以对 在未完成的显式或隐式启动中使用的表 另一个会话中的事务。服务器通过获取 事务中使用的表上的元数据锁定和延迟 释放这些锁,直到交易结束。元数据锁定 表可防止更改表的结构。此锁定 方法意味着 一个会话中的事务不能在 DDL 语句中使用 其他会话,直到事务结束。
这是有道理的,所以我做了这个测试:
connectionA$ begin;
connectionA$ select * from facebook_authorizations;
connectionA$ ....
connectionB$ alter table facebook_authorizations add column foo int default null;
connectionC$ begin;
connectionC$ select * from facebook_authorizations;
connectionA$ commit;
在我的系统上,当连接 A 提交时,连接 C 执行并且连接 B 仍然挂起:它被基于 SELECT 的转换所剥夺执行。我期待元数据锁定等待列表大致按照 FIFO 顺序处理,但似乎并非如此。
是否有关于元数据等待队列处理顺序的文档?
有关方案的详细信息:
试图重现案例,我阻止了会话 B,然后会话 C 被阻止...... 请注意,如果会话 B 实际上在获取表上的元数据锁之前正在等待其他内容,则会话 C 没有理由等待。
现在,一般来说:
已经授予的元数据锁在表metadata_locks
的performance_schema
中可见,LOCK_STATUS
为GRANTED
。
请参阅文档: https://dev.mysql.com/doc/refman/8.0/en/metadata-locks-table.html
这有助于查看哪个会话拥有哪个锁。
会话正在等待的元数据锁也在同一表中可见,LOCK_STATUS
为PENDING
。
这有助于查看会话正在等待的内容。
一个(被阻止的(会话等待锁定某些东西,而该锁定又可能已经被其他会话锁定(具有各种LOCK_TYPE
和LOCK_DURATION
(,但这里没有直接的"会话 X 等待会话 Y"关系,这是由已经到位的锁所暗示的。
当多个会话都等待相同的资源,并且资源可用(会话释放元数据锁(时,尝试预测处理顺序(在我看来(是有风险的,应用程序逻辑不应依赖于此:据我所知,当前的实现确实是 FIFO,但这可能随时更改, 并且没有记录在案。
这里的理由是服务器必须具有一定程度的自由度,以便实现不同的调度策略(例如出于性能原因(是可行的。如果某个应用程序以某种方式"期望"给定的订单,它将中断并阻止任何更改。