问题1:set global read_only=ON和FTWRL有什么区别?
问题2:DML和DDL语句加锁逻辑有区别么?
问题3:什么场景出现MDL死锁?
针对上面三个问题,展开关于DML锁的学习与讨论。本次主要讨论MDL锁,所以引擎层面的锁不做探究。
现在回想一下,这三个问题点可以从两个层面回答?
- 基于MDL锁机制和兼容性
- 对于运维的影响?
MDL加锁表示我们简化成namespace-MDL Lock duration -MDL lock type
格式;
GLOBAL-STATEMENT-INTENTION_EXCLUSIVE
简化为
GLOBAL-STATEMENT-IX
意思是namespace为Global的IX锁,锁持续周期是STATEMENT,也就是语句执行完就释放。基本概念会在后面讲述。
小试牛刀,先看看第一个问题:
首先FTWRL上锁有五个步骤
- 加
GLOBAL-EXPLICIT-S
,如果发生阻塞,表现为Waiting for global read lock
; - 推进全局表缓存版本。
- 释放没有使用的table缓存
- 判断是否存在正在使用的table 缓存,等待状态为
Waiting for table flush
。这一步会判断table缓存的版本和全局表缓存版本是否匹配,如果不匹配则等待。 - 加
COMMIT-EXPLICIT-S
。这一步发生阻塞,现象为Waiting for commit lock
。
我们从影响上简化步骤就三步:
- 获取GLOBAL-STATEMENT-S
- 推进全局表缓存版本,释放没有使用的table缓存
- 获取COMMIT-STATEMENT-S
set global read_only=ON会获取两把锁,主要调用函数是fix_read_only:
- GLOBAL-STATEMENT-S
- COMMIT-STATEMENT-S
那其实看完这两段,已经发现了read_only和FTWRL之间的上锁区别了。不过需要完整回答上面三个问题,进一步了解MDL锁机制。
1. MDL锁基本概念
MDL(Metadata Lock)锁的主要目的是为了保证数据库元数据(如表结构、数据库、索引等)的一致性和安全性 。
MDL也有死锁,检测逻辑和机制跟InnoDB差不多。
下面先把MDL锁涉及到的一些基本概念过一下:
- MDL Lock Type
MDL_INTENTION EXCLUSIVE (IX)
MDL_SHARED(S)
MDL_SHARED_HIGH_PRIO(SH)
MDL_SHARED_READ(SR)
MDL_SHARED_WRITE(SW)
MDL_SHARED_WRITE_LOW_PRIO(SWL)
MDL_SHARED_UPGRADABLE(SU)
MDL_SHARED_READ_ONLY(SRO)
MDL_SHARED_NO_WRITE(SNW)
MDL_SHARED_NO_READ_WRITE(SNRW)
MDL_EXCLUSIVE(X)
2. MDL Lock namespace:
在 MDL 中,MDL_KEY以namespace+DB+OBJECT_NAME的方式表示。namespace的类型如下;
/**
Object namespaces.
Sic: when adding a new member to this enum make sure to
update m_namespace_to_wait_state_name array in mdl.cc!
Different types of objects exist in different namespaces
- GLOBAL is used for the global read lock.
- BACKUP_LOCK is to block any operations that could cause
inconsistent backup. Such operations are most DDL statements,
and some administrative statements.
- TABLESPACE is for tablespaces.
- SCHEMA is for schemas (aka databases).
- TABLE is for tables and views.
- FUNCTION is for stored functions.
- PROCEDURE is for stored procedures.
- TRIGGER is for triggers.
- EVENT is for event scheduler events.
- COMMIT is for enabling the global read lock to block commits.
- USER_LEVEL_LOCK is for user-level locks.
- LOCKING_SERVICE is for the name plugin RW-lock service
- SRID is for spatial reference systems
- ACL_CACHE is for ACL caches
- COLUMN_STATISTICS is for column statistics, such as histograms
- RESOURCE_GROUPS is for resource groups.
- FOREIGN_KEY is for foreign key names.
- CHECK_CONSTRAINT is for check constraint names.
Note that requests waiting for user-level locks get special
treatment - waiting is aborted if connection to client is lost.
*/
3. MDL Lock的实现分类:
定义一个结构体,描述不同类型的锁如何处理特定的 MDL_lock。在实际应用中,我们只使用两种策略:对于 GLOBAL、COMMIT、TABLESPACE 和 SCHEMA 命名空间中的锁使用“scoped”锁策略,对于所有其他命名空间使用“object”锁策略。
-
- scope lock:
- object lock:
- MDL Lock兼容矩阵:
兼容矩阵分为scope lock和object lock。
-
- scope lock
| Type of active |
Request | scoped lock |
type | IS(*) IX S X |
---------+------------------+
IS | + + + + |
IX | + + - - |
S | + - + - |
X | + - - - |
-
- object lock
Request | Granted requests for lock |
type | S SH SR SW SWLP SU SRO SNW SNRW X |
----------+---------------------------------------------+
S | + + + + + + + + + - |
SH | + + + + + + + + + - |
SR | + + + + + + + + - - |
SW | + + + + + + - - - - |
SWLP | + + + + + + - - - - |
SU | + + + + + - + - - - |
SRO | + + + - - + + + - - |
SNW | + + + - - - + - - - |
SNRW | + + - - - - - - - - |
X | - - - - - - - - - - |
这里需要注意,我们在判断锁是否冲突时要根据对应的锁范围来决定,比如获取了一个namespace为GLOBAL的S锁和namespace 为 TABLE 的 S的锁,这是两个层面的锁。并不存在冲不冲突的问题。因为GLOBAL属于scope lock范围,TABLE 属于object lock 范围。
- MDL Lock duration(MDL Lock持续周期)
-
- MDL_STATEMENT:具有语句持续时间的锁会在语句或事务结束时自动释放。
- MDL_TRANSACTION:具有事务持续时间的锁会在事务结束时自动释放。
- MDL_EXPLICIT:具有显式持续时间的锁在语句和事务结束时仍然存在。它们必须通过显式调用
MDL_context::release_lock()
来释放。
每个语句获取到的锁的持续周期是不一样的,
- MDL_request结构的部分属性:
-
- 语句解析后需要获得MDL Lock,通过这个类对象在MDL子系统中进行MDL Lock申请
- MDL_key结构的部分属性
-
- MDL_key使用namespace+DB+OBJECT_NAME表示,namespace+DB+OBJECT_NAME整个放到一个char数组里面,它会在MDL_lock和MDL_request中出现。
- MDL_ticket结构的部分属性
-
- 如同门票一样,如果会话获取了MDL Lock,则必然给MDL_request返回一个MDL_ticket,如果等待,则不会分配。
- MDL_lock结构的部分属性
-
- 每一个MDL_key都会对应一个MDL_lock,其中包含了GRANTED链表和WAIT链表
- MDL_context结构的部分属性
-
- 这是整个MySQL线程和MDL Lock子系统进行交互的上下文结构
- 所有等待状态:
(0,"Waiting for global read lock", 0),
(0, "Waiting for tablespace metadata lock", 0),
(0, "Waiting for schema metadata lock", 0),
(0, "Waiting for table metadata lock", 0),
(0, "Waiting for stored function metadata lock", 0},
(0, "Waiting for stored procedure metadata lock", 0),
(0, "Waiting for trigger metadata lock", 0),
(0, "Waiting for event metadata lock", 0),
(0, "Waiting for commit lock", 0},
(0, "User lock", 0), /* Be compatible with old status. */
(0, "Waiting for locking service lock", 0},
(0, "Waiting for backup lock", 0},
(0, "Waiting for binlog lock", 0)
其中3个常见的等待状态:
-
- Waiting for table metadata lock
- Waiting for global read lock
- Waiting for commit lock
现在应该可以看懂GLOBAL-STATEMENT-IX
这种表示方式了。
2. 常见MDL Lock类型的加锁测试
对于MDL Lock duration为MDL_STATEMENT类型,无法通过performance_schema视图观察,所以这里直接引用书籍中打印的日志。
这部分需要在源码中增加打印函数,具体打印步骤可以参考《深入理解MySQL主从原理》5.3.3和5.3.4章节。
当然,若需要通过pfs观察,需要开启PFS相关参数
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME ='global_istrumentation';
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME='wait/lock/metadata/sql/mdl';
#查看当前元数据锁信息,排除自身线程
select * from performance_schema.metadata_locks where owner_thread_id<>sys.ps_thread_id(connection_id());
2.1. MDL_INTENTION_EXCLUSIVE(IX)
这个锁在很多操作中都会出现,比如任何一个 DML、DDL 操作都会触发它,包括for update等操作会在GLOBAL上加IX锁,然后才会在对象上加锁。而DDL语句至少会在GLOBAL上加IX锁,在对象所属 SCHEMA上加IX锁,然后进行对象加锁。
DELETE 触发的 GLOABL IX MDL Lock 执行过程。
MDL Lock duration:MDL_STATEMENT
2.2. MDL_SHARED(S)
这把锁一般用在flush tables with read lock中。
MDL Lock duration:MDL_EXPLICIT
2.3. MDL_SHARED_HIGH_PRIO(SH)
MDL_SHARED_HIGH_PRIO锁经常被用到,比如在desc操作中
MDL Lock duration:MDL_TRANSACTION
2.4. MDL_SHARED_READ(SR)
MDL_SHARED_READ一般用在非当前读的select中
MDL Lock duration:MDL_TRANSACTION
2.5. MDL_SHARED_WRITE(SW)
MDL_SHARED_WRITE一般用于DELTE、UPDATE、INSERT、for update等操作对table的加锁(当前读),不包含DDL操作。
注意DML操作实际上还会有一个GLOBAL的IX锁(scopes lock),这把锁只是对象上的(object lock)
MDL Lock duration:MDL_TRANSACTION
2.6. MDL_SHARED_WRITE_LOW_PRIO(SWLP)
很少用到。
2.7. MDL_SHARED_UPGRADABLE(SU)
一般在ALTER TABLE语句中会用到,可以升级为SNW、SNRW、X,同时,至少X锁也可以降级为SU。
MDL Lock duration:MDL_TRANSACTION
2.8. MDL_SHARED_NO_WRITE(SNW)
ALGORITHM=COPY 在COPY阶段用的是SNW锁
MDL Lock duration:MDL_TRANSACTION
2.9. MDL_SHARED_READ_ONLY(SRO)
MDL_SHARED_READ_ONLY用于LOCK TABLES READ 语句
MDL Lock duration:MDL_TRANSACTION
2.10. MDL_SHARED_NO_READ_WRITE(SNRW)
MDL_SHARED_NO_READ_WRITE用于LOCK TABLES WRITE语句
MDL Lock duration:MDL_TRANSACTION
2.11. MDL_EXCLUSIVE(X)
几乎所有的DDL都会涉及MDL_EXCLUSIVE,即便是ONLINE DDL(ALGORITHM=INPLACE),也会在准备和提交阶段获取本锁,因此ONLINE DDL(ALGORITHM=INPLACE)不是完全不堵塞其他操作,只是堵塞的时间很短
总结:
MDL Lock 类型 | 锁持续时间 | 使用场景 |
---|---|---|
MDL_INTENTION_EXCLUSIVE(IX) | MDL_STATEMENT | 触发 DML 和 DDL 操作,DELETE 、UPDATE 、INSERT 、FOR UPDATE 等 DML 操作会在 GLOBAL 上加 IX 锁,然后才会在对象上加锁;DDL 语句至少会在 GLOBAL 上加 IX 锁,在对象所属 SCHEMA 上加 IX 锁,然后进行对象加锁。 |
MDL_SHARED(S) | MDL_STATEMENT | 一般用于 FLUSH TABLES WITH READ LOCK 操作,涉及 GLOBAL 和 COMMIT namespace。 |
MDL_SHARED_HIGH_PRIO(SH) | MDL_EXPLICIT | 经常用于 DESC 操作等需要高优先级共享读锁的情况。 |
MDL_SHARED_READ(SR) | MDL_TRANSACTION | 用于非当前读的 SELECT 操作。 |
MDL_SHARED_WRITE(SW) | MDL_TRANSACTION | 用于 DELETE 、UPDATE 、INSERT 、FOR UPDATE 等操作对表的加锁(当前读),不包含 DDL 操作。 |
MDL_SHARED_WRITE_LOW_PRIO(SWLP) | MDL_TRANSACTION | 很少用到。 |
MDL_SHARED_UPGRADABLE(SU) | MDL_TRANSACTION | 一般用于 ALTER TABLE 语句,可升级为 SNW 、SNRW 、X ,也可以降级为 SU 。DML(SW)和 SELECT (SR)不会堵塞。 |
MDL_SHARED_NO_WRITE(SNW) | MDL_TRANSACTION | 在 ALGORITHM=COPY 的 COPY 阶段使用,保护数据的一致性。 |
MDL_SHARED_READ_ONLY(SRO) | MDL_TRANSACTION | 用于 LOCK TABLES READ 语句。 |
MDL_SHARED_NO_READ_WRITE(SNRW) | MDL_TRANSACTION | 用于 LOCK TABLES WRITE 语句。 |
MDL_EXCLUSIVE(X) | MDL_TRANSACTION | 几乎所有的 DDL 操作都会涉及 MDL_EXCLUSIVE ,即使是 ONLINE DDLALGORITHM=INPLACE)也会在准备和提交阶段获取此锁。 |
补充一个优先级列表:
- Scope锁等待锁和请求锁优先级矩阵
| Pending |
Request | scoped lock |
type | IS(*) IX S X |
---------+-----------------+
IS | + + + + |
IX | + + - - |
S | + + + - |
X | + + + + |
+号表示请求的锁可以满足。
-号表示请求的锁无法满足需要等待。
- object上等待锁和请求锁的优先级矩阵
Request | Pending requests for lock |
type | S SH SR SW SWLP SU SRO SNW SNRW X |
----------+--------------------------------------------+
S | + + + + + + + + + - |
SH | + + + + + + + + + + |
SR | + + + + + + + + - - |
SW | + + + + + + + - - - |
SWLP | + + + + + + - - - - |
SU | + + + + + + + + + - |
SRO | + + + - + + + + - - |
SNW | + + + + + + + + + - |
SNRW | + + + + + + + + + - |
X | + + + + + + + + + + |
3. 问题回答
问题1:set global read_only=ON和FTWRL有什么区别?
从加锁模式以及运维影响两个角度回答:
- 加锁模式上FTWRL与read_only都需要GLOBAL-EXPLICIT-S和COMMIT-EXPLICIT-S锁,其中FTWRL多一个步骤就是推进全局表缓存版本,如果此时有一些表无法关闭,那么会进入waiting for table flush。注意这个等待并不是阻塞,而是FTWRL等待其他线程释放表缓存,当其他线程释放表缓存之后就会唤醒FTWRL线程继续下一步。
- 从运维层面,FTWRL会随着线程退出而释放,但是read_only不会。FTWRL的锁危害是大于read_only的。因为推进全局表缓存版本并不是原子性,而且他的执行位置是在GLOBAL-STATEMENT-S和COMMIT-STATEMENT-S锁之间,具体案例可以参考《深入理解MySQL主从原理》中5.4章节“奇怪的FTWRL堵塞案例”。
问题2:DML和DDL语句加锁逻辑有区别么?
DDL 的整体执行过程
- 准备阶段(prepare),DDL 前的准备工作。
- 执行阶段(do DDL),DDL 的执行过程。按照执行逻辑分类,MySQL DDL 的执行过程分为 Copy DDL 和 Inplace DDL 两类,8.0 新支持的 Instant DDL 也划分到 Inplace DDL 中。如果是 Copy DDL,直接在 Server 层完成;如果是 Inplace DDL,需要进入 InnoDB 层执行。InnoDB 层的执行逻辑同样也会分为三个阶段(主要由mysql_inplace_alter_table函数实现)。
-
- ha_prepare_inplace_alter_table
- ha_inplace_alter_table
- ha_commit_inplace_alter_table
- 收尾阶段(cleanup),DDL 后的清理工作。
DML概念就不用说了,我们定义DML包含了select/insert/update/delete 这四种常见的语句,当然有一些描述中select归类为DQL,不做纠结。其次INSERT ... ON DUPLICATE KEY UPDATE
这一类命令本次不做研究。
只研究DML层面的锁,不涉及InnoDB层面(根据不同的语句类型以及是否有主键或索引,还有当前的隔离级别的不同,上锁方式都会有变化,后面补一章InnoDB的锁相关内容)。
DDL根据算法可以分成三类:
- COPY
- INPLACE
- INSTANT
下面开始分析DML和DDL加锁过程:
- select
-
- TABLE-TRANSACTION-SR
- insert/update/delete
-
- 在open_table阶段获取GLOBAL-STATEMENT-IX、TABLE-TRANSACTION-SW
- 在commit阶段获取COMMIT-MDL_EXPLICIT-IX
- COPY:全部在Server层完成,所以只需要prepare、do DDL、cleanup阶段;其他DDL算法也按此类方式分类:
-
- prepare阶段: 加GLOBAL-STATEMENT-IX锁,SCHEMA-TRANSACTION-IX锁,TABLE-TRANSACTION-SU锁
- do DDL阶段: 将TABLE-TRANSACTION-SSU锁升级到SNW;数据拷贝完成之后,在rename表之前将SU锁升级成X锁。
- cleanup: 做清空动作,包括锁的释放。
- INPLACE
-
- prepare阶段: 加GLOBAL-STATEMENT-IX锁,SCHEMA-TRANSACTION-IX锁,TABLE-TRANSACTION-SU锁
- do DDL:将TABLE-TRANSACTION-SU升级为TABLE-TRANSACTION-X锁。(调用mysql_inplace_alter_table函数)
-
-
- ha_prepare_inplace_alter_table:锁降级为 SU
- ha_inplace_alter_table:锁升级为 X
- ha_commit_inplace_alter_table:
-
-
- Cleanup: 做清空动作,包括锁的释放。其实在ha_commit_inplace_alter_table阶段做的,我们统一放在这里描述。
- INSTANT
-
- prepare阶段: 加GLOBAL-STATEMENT-IX锁,SCHEMA-TRANSACTION-IX锁,TABLE-TRANSACTION-SU锁
- do DDL:
-
-
- ha_prepare_inplace_alter_table:无锁
- ha_inplace_alter_table:TABLE-TRANSACTION-SU 锁升级为 TABLE-TRANSACTION-X
- ha_commit_inplace_alter_table:更新元数据即可
-
-
- Cleanup: 做清空动作,包括锁的释放。其实在ha_commit_inplace_alter_table阶段做的,我们统一放在这里。
我们整理成表格:
操作类型 | 加锁类型 |
---|---|
SELECT | TABLE-TRANSACTION-SHARED_READ |
INSERT/UPDATE/DELETE | GLOBAL-STATEMENT-INTENTION_EXCLUSIVETABLE-TRANSACTION-SHARED_WRITECOMMIT-MDL_EXPLICIT-INTENTION_EXCLUSIVE |
DDL/COPY | GLOBAL-STATEMENT-INTENTION_EXCLUSIVESCHEMA-TRANSACTION-INTENTION_EXCLUSIVETABLE-TRANSACTION-SHARED_UPGRADABLETABLE-TRANSACTION-SHARED_NO_WRITETABLE-TRANSACTION-EXCLUSIVE |
DDL/INPLACE | GLOBAL-STATEMENT-INTENTION_EXCLUSIVESCHEMA-TRANSACTION-INTENTION_EXCLUSIVETABLE-TRANSACTION-SHARED_UPGRADABLETABLE-TRANSACTION-EXCLUSIVE |
DDL/INSTANT | GLOBAL-STATEMENT-INTENTION_EXCLUSIVESCHEMA-TRANSACTION-INTENTION_EXCLUSIVETABLE-TRANSACTION-SHARED_UPGRADABLETABLE-TRANSACTION-EXCLUSIVE |
问题3:什么场景出现MDL死锁?
给个提示,MTS并行回放场景。这里不做详细解释了。
4. 语句阻塞关系图谱
好,那么我们在运维过程中,经常出现的问题是DML(包含Select)、DDL语句,包括FTWRL这类语句之间的阻塞。下面根据上面的知识整理出阻塞关系图谱:
先把影响最大的FTWRL补充上去,DDL部分后续补充(先跳票。。。)。
被谁阻塞?
命令 | 被谁阻塞 | 阻塞点 | 等待状态 |
---|---|---|---|
FTWRL | 长时间DDL/DML/SELECT FOR UPDATE | GLOBAL S 被 GLOBAL IX 阻塞 | Waiting for global read lock |
长时间的select(快照读) | 等待释放表缓存 | Waiting for table flush | |
长时间COMMIT | COMMIT S 被 COMMIT IX 阻塞 | Waiting for commit lock | |
DDL-COPY | |||
DDL-INPLACE | |||
DDL-INSTANT |
阻塞谁?
命令 | 阻塞谁 | 阻塞点 | 等待状态 |
---|---|---|---|
FTWRL | DDL/DML/SELECT FOR UPDATE | GLOBAL S 阻塞 GLOBAL IX 阻塞 | Waiting for global read lock |
COMMIT | COMMIT S 阻塞 COMMIT IX 阻塞 | Waiting for commit lock | |
DDL-COPY | |||
DDL-INPLACE | |||
DDL-INSTANT |
最后附上DDL相关操作列表:
DDL操作列表
操作 | 版本 | INSTANT | INPLACE | 重建表 | 并发 DML | 仅修改元数据 |
---|---|---|---|---|---|---|
二级索引 | ||||||
创建二级索引 | MySQL 8.0 | No | Yes | No | Yes | No |
MySQL 5.7 | Yes | No | Yes | No | ||
MySQL 5.6 | Yes | No | Yes | No | ||
删除索引 | MySQL 8.0 | No | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
重命名索引 | MySQL 8.0 | No | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | ||||||
增加全文索引 | MySQL 8.0 | No | Yes* | No* | No | No |
MySQL 5.7 | Yes* | No* | No | No | ||
MySQL 5.6 | Yes* | No* | No | No | ||
增加空间索引 | MySQL 8.0 | No | Yes | No | No | No |
MySQL 5.7 | Yes | No | No | No | ||
MySQL 5.6 | ||||||
修改索引类型 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
主键 | ||||||
增加主键 | MySQL 8.0 | No | Yes* | Yes* | Yes | No |
MySQL 5.7 | Yes* | Yes* | Yes | No | ||
MySQL 5.6 | Yes* | Yes* | Yes | No | ||
删除主键 | MySQL 8.0 | No | No | Yes | No | No |
MySQL 5.7 | No | Yes | No | No | ||
MySQL 5.6 | No | Yes | No | No | ||
重建主键 | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
列操作 | ||||||
新增列 | MySQL 8.0 | Yes* | Yes | No* | Yes* | No |
MySQL 5.7 | Yes | Yes | Yes* | No | ||
MySQL 5.6 | Yes | Yes | Yes* | No | ||
删除列 | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
重命名列 | MySQL 8.0 | No | Yes | No | Yes* | Yes |
MySQL 5.7 | Yes | No | Yes* | Yes | ||
MySQL 5.6 | Yes | No | Yes* | Yes | ||
调整列顺序 | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
修改列默认值 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
修改列数据类型 | MySQL 8.0 | No | No | Yes | No | No |
MySQL 5.7 | No | Yes | No | No | ||
MySQL 5.6 | No | Yes | No | No | ||
扩展 VARCHAR 长度 | MySQL 8.0 | No | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | ||||||
删除列默认值 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
修改自增值 | MySQL 8.0 | No | Yes | No | Yes | No* |
MySQL 5.7 | Yes | No | Yes | No* | ||
MySQL 5.6 | Yes | No | Yes | No* | ||
修改列为空 | MySQL 8.0 | No | Yes | Yes* | Yes | No |
MySQL 5.7 | Yes | Yes* | Yes | No | ||
MySQL 5.6 | Yes | Yes* | Yes | No | ||
修改列为非空 | MySQL 8.0 | No | Yes* | Yes* | Yes | No |
MySQL 5.7 | Yes* | Yes* | Yes | No | ||
MySQL 5.6 | Yes* | Yes* | Yes | No | ||
修改列 ENUM 值 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes | ||
表操作 | ||||||
修改 ROW_FORMAT | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
修改 KEY_BLOCK_SIZE | MySQL 8.0 | No | Yes | Yes | Yes | No |
MySQL 5.7 | Yes | Yes | Yes | No | ||
MySQL 5.6 | Yes | Yes | Yes | No | ||
指定字符集 | MySQL 8.0 | No | Yes | Yes* | No | No |
MySQL 5.7 | Yes | Yes* | No | No | ||
MySQL 5.6 | Yes | Yes* | No | No | ||
修改字符集 | MySQL 8.0 | No | No | Yes* | No | No |
MySQL 5.7 | No | Yes* | No | No | ||
MySQL 5.6 | No | Yes | No | No | ||
OPTIMIZE 表 | MySQL 8.0 | No | Yes* | Yes | Yes | No |
MySQL 5.7 | Yes* | Yes | Yes | No | ||
MySQL 5.6 | Yes* | Yes | Yes | No | ||
重命名表 | MySQL 8.0 | Yes | Yes | No | Yes | Yes |
MySQL 5.7 | Yes | No | Yes | Yes | ||
MySQL 5.6 | Yes | No | Yes | Yes |
本次文章主要讲述MDL锁原理以及加锁过程,如果从锁的层面还需要补充InnoDB锁以及在发生锁的时候如何解决。期待:
- MySQL 锁三部曲(2) - InnoDB 锁
- MySQL 锁三部曲(3) - 锁运维
参考:
《深入理解MySQL主从原理》中5.4章节“奇怪的FTWRL堵塞案例”和5.3章节“MySQL中的MDL Lock简介”
MySQL · 源码分析 · 常用SQL语句的MDL加锁源码分析
白话 MySQL Online DDL 2 · 从 INT 列转 BIGINT 锁表看 MySQL DDL 执行过程
dev.mysql.com/doc/refman/… dev.mysql.com/doc/refman/… dev.mysql.com/doc/refman/…