MySQL的在线模式学习笔记

什么是在线模式(ONLINE DDL)?

"在线模式 / 离线模式"是 MySQL 专门为 DDL 设计的术语。ONLINE DDL 定义其实就是在执行 DDL 语句时,不会阻塞 DML 语句,那么我们就称这样的 DDL 为ONLINE DDL。

什么是离线模式?

**离线模式**是指在 MySQL 5.5 及更早版本中执行 DDL 操作时,表会被完全锁定,所有读写操作将被阻塞,直到操作完成。在离线 DDL 执行期间,需要修改的表会被完全锁定,这意味着所有对该表的读写操作都会被阻塞,直到 DDL 操作完成。由于表被锁定,任何依赖该表的应用程序都将无法正常工作,导致服务中断或延迟。对于高并发、7x24小时运行的线上系统而言,这种长时间的停机是不可接受的。

**在线模式**是 MySQL 5.6 及更高版本引入的重大改进 ,旨在解决离线模式带来的服务中断问题。它的核心思想是允许在DDL操作进行的同时,对表进行并发的读写操作,从而最大程度地保证数据库的可用性。

以下内容来自:MySQL Reference Manual --- Online DDL Operations

在线 DDL 功能提供对原地表修改和并发 DML 的支持。此功能的优势包括:

  • 在繁忙的生产环境中提高响应能力和可用性,因为使表不可用几分钟或几小时是不切实际的。
  • 能够在 DDL 操作期间使用 LOCK 子句调整性能和并发之间的平衡。参见 LOCK 子句
  • 比表复制方法使用的磁盘空间更少,I/O 开销也更小。

通常,您无需执行任何特殊操作即可启用在线 DDL。默认情况下,MySQL 在允许的情况下就地执行操作,并尽可能减少锁定。

您可以使用 ALGORITHMLOCK 语句的子句来控制 DDL 操作的各种方面。这些子句位于语句的末尾,通过逗号与表和列规范分开。例如:

sql 复制代码
ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE;

版本演进

MySQL Online DDL 功能自 5.6 版本正式引入,并在后续版本不断完善:

  • MySQL 5.5 :MySQL 5.5 及之前,所有的 DDL 操作都是全程锁表(LOCK = EXCLUSIVE),仅支持 Fast Index Create(快速索引创建)。此时对二级索引的增删 操作可以使用 Inplace 算法,从而避免重建整个表,但仍需对表加锁,阻塞写操作。其他变更仍只能采用复制表的方式。但是因为实现的问题,依然会阻塞 INSERT、UPDATE、DELETE 操作,造成的问题是:如果是在业务高峰期执行一条 DDL 语句,往往有明显的业务中断,从而出现直接引发"雪崩报警",这也是 MySQL 早期版本长期被吐槽的原因之一。
  • MySQL 5.6 :正式引入 Online DDL 概念,官方显著增加了可在线的 ALTER TABLE 类型,并允许在 DDL 执行时并发 DML,支持在不阻塞并发 DML 的情况下添加索引、修改自增值等操作。旨在显著减少(或消除)更改数据库对象所需的应用程序停机时间(该特性以下统称 Online DDL),从而减少重建表的需要。不过,并非所有 DDL 都支持在线模式,具体支持可以参考 各版本支持的详细情况 章节。
  • MySQL 5.7:在 5.6 的基础上扩展了对更多 DDL 操作的在线支持,例如支持在线重命名索引、在线增减数值类型长度、在线扩展 VARCHAR 长度等操作。但整体实现逻辑与 5.6 相似,依旧采用 Copy 和 Inplace 两种方式。
  • MySQL 8.0 :大幅重构了 DDL 执行引擎,支持原子性 DDL(失败可回滚),并新增 ALGORITHM=INSTANT 算法。在 8.0.12+ 中,支持将列"秒加"到表的末尾,无需重建表;8.0.29 起 Instant 算法成为默认,可在任意位置添加或删除列。同时,MySQL 8.0 对 ALTER TABLE操作进行了重构与优化,引入 LOCK=NONE|SHARED|EXCLUSIVE参数来更灵活地控制锁粒度和并发行为。

MySQL Online DDL详解:从历史演进到原理及使用

MySQL 8.0 新特性之 DDL 即时加列

Online DDL 解决的问题

Online DDL 的核心在于让 DDL 操作与 DML(查询/增删改)并发执行,从而避免长时间锁表。

原理

MySQL Online DDL 的底层实现主要依赖三种算法:COPYINPLACE,以及在 MySQL 8.0.12+ 中引入的 INSTANT

  • 复制表法(Copy Table)

COPY 是 Online DDL 中最常见的一种实现方式,它在后台模拟了传统 DDL 的重建流程。但在后台进行,从而减少了对在线业务的影响。其核心步骤如下:

markdown 复制代码
- **创建临时表:** MySQL会创建一个与原始表结构相同的新临时表。
- **复制数据:** 将原始表中的数据复制到临时表中。 在数据复制期间,所有对原始表的 DML(数据操作语言)操作(INSERT、UPDATE、DELETE) 都会被捕获并记录为增量变更。  
- **应用增量变更:** 当数据复制完成后,MySQL会暂停对原始表的DML操作一小段时间,然后将复制期间记录的所有增量DML操作应用到临时表上。
- **原子性替换:** 最后,MySQL会通过原子性的操作将原始表重命名,并将临时表重命名为原始表的名称。这个替换操作通常非常快,对业务的影响极小。

这种方式的优点是实现简单,兼容性好,但缺点是需要额外的磁盘空间(至少是原始表大小的一倍),并且在数据复制期间会消耗CPU和I/O资源。

  • 原地变更法(In-Place)

对于某些特定的DDL操作(如添加、删除或重命名列,添加或删除索引等),MySQL可以采用更高效的"原地变更"方式,即直接在原表上进行修改,而不需要创建临时表和复制数据。这种方式的效率更高,对系统资源的消耗更小,因为它避免了大量的数据复制。

原地变更的实现机制通常涉及对表的数据文件和索引文件进行直接修改。例如,添加新列可能只需要修改表的元数据信息,而不需要重写所有行数据。然而,并不是所有的DDL操作都能使用原地变更。

1 创建frm(表结构定义文件)临时文件
2 锁原表,不允许DML(数据操作语言),允许查询
3 根据聚集索引顺序构建新的索引项,按照顺序插入新的索引页
4 原表升级锁,禁止读写,即原表暂停服务
5 rename操作,替换原表的frm文件

根据是否需要重建表结构,又分为两种情况:

  • 需要重建(Rebuild):如添加索引、添加/删除列、修改列的 NULL 属性等操作需要重新组织聚簇索引。在此过程中,InnoDB 会后台构建新的表数据,同时缓存并发执行的 DML 操作。DDL 完成后再将缓存的 DML 增量应用到表上,实现变更和业务并行。
  • 无需重建(No-Rebuild):如删除索引、修改列名或默认值、修改自增值等操作只需更新表元数据即可,不改动行数据。

在 INPLACE 算法下,InnoDB 仍会对原表加元数据锁(MDL)以保证一致性:在数据转换阶段通常加 MDL 读锁(允许 DML,但不允许其他 DDL),在最后合并增量时加 MDL 写锁(阻止并发 DDL/DML)。由于数据并未真正移动到外部临时表,该过程对服务器来说是"原地"操作,减少了 IO 和空间开销。

  • INSTANT 算法

只支持极少数简单操作(如修改字符集或在表末尾添加列)。此时 MySQL 仅修改数据字典中的元数据条目,不对表数据作任何变动,也无需表锁。由于仅更新元信息,所以DDL 几乎瞬间完成,对业务没有任何阻塞。例如,MySQL 8.0.12 引入了 Instant 算法用于快速加列(仅限末尾列),8.0.29 起将其作为默认算法并支持任意位置添加和快速删除列。

说明: 在 Online DDL 过程中,为了保证数据一致性,MySQL 会对原表加上短暂的元数据锁。如在 COPY 或 INPLACE 的复制阶段对原表加 MDL 读锁,在应用增量阶段加 MDL 写锁;在最终替换表结构时,会再短暂地加独占锁重命名表。通过这种方式,Online DDL 保证了在大部分时间内业务可并发访问,只有在关键切换点才加锁。

关键参数:ALGORITHMLOCK

ALTER TABLE语句中,我们可以通过ALGORITHMLOCK子句来控制DDL操作的行为:

  • ALGORITHM: 指定DDL操作的算法,可选值有:
    • INPLACE: 尝试原地变更,如果不支持则报错。
    • COPY: 强制使用复制表法。
    • INSTANT: (MySQL 8.0引入)某些操作可以在瞬间完成,无需重建表。
    • DEFAULT: MySQL会自动选择最佳的算法,优先使用 INPLACE 的方式。
  • LOCK: 指定DDL操作的锁定级别,可选值有:
    • NONE: 允许并发读写。
    • SHARED: 共享锁,允许并发读,但禁止并发写。
    • EXCLUSIVE: 独占锁,禁止并发读写,指定 DDL 的表将被完全锁定,无法执行任何读写操作。
    • DEFAULT: MySQL会自动选择最佳的锁定级别。

通常情况下,我们应该尽量选择ALGORITHM=INPLACELOCK=NONE,以实现最少的锁定和最高的可用性。

使用示例

在 ALTER TABLE 语句中,可以通过 ALGORITHMLOCK 子句来启用和控制在线 DDL。例如,官方文档示例中提到使用原地算法:

sql 复制代码
ALTER TABLE tbl_name ADD PRIMARY KEY (col), ALGORITHM = INPLACE;

用户可以按需设置:如以下命令示例在添加列时尽量使用原地算法并允许并发读写:

sql 复制代码
ALTER TABLE mytable ADD COLUMN new_col INT DEFAULT 0 ALGORITHM = INPLACE, LOCK = NONE;

上述命令尝试在原表上增列并同时允许并发读写developer.aliyun.com。对于 MySQL 8.0.12 及以上版本,也可指定 ALGORITHM=INSTANT(仅限支持 Instant 的操作)来实现几乎即时的变更:

sql 复制代码
ALTER TABLE mytable  ADD COLUMN new_col2 INT DEFAULT 1  ALGORITHM = INSTANT;

需要注意,Instant 算法仅支持在允许的位置(例如末尾)添加或删除列MySQL 8.0 新特性之 DDL 即时加列;如果不指定算法(或指定 ALGORITHM=DEFAULT),MySQL 会优先尝试 INPLACE,不行时回退到 COPY 模式

注意事项

集群模式下存在问题

Master-Slave架构下,官方的Online DDL存在致命的缺陷,所以我们只能转向第三方工具。这里有Percona的公司的pt-osc,是基于触发器的Online DDL的代表。以及比较新的,Github的gh-ost,是基于Binlog的。

gh-ost(Binlog 增量同步,无触发器)

pt-online-schema-change(触发器方式)

MySQL Online DDL 探究

针对生产环境,对于需要频繁读写的"大表",执行 DDL 操作时,不推荐使用 MySQL 的在线模式,推荐使用第三方工具:

github.com/github/gh-o...

github.com/percona/per...

docs.percona.com/percona-too...

Reference

MySQL Online DDL详解:从历史演进到原理及使用

mysql 大表如何ddl

dev.mysql.com/doc/refman/...

各版本支持的详细情况

doc.yonyoucloud.com/doc/mysql-5...

dev.mysql.com/doc/refman/...

dev.mysql.com/doc/refman/...

相关推荐
WanderInk32 分钟前
在递归中为什么用 `int[]` 而不是 `int`?——揭秘 Java 参数传递的秘密
java·后端·算法
why技术36 分钟前
哎,我糊涂啊!这个需求居然没想到用时间轮来解决。
java·后端·面试
青草地溪水旁41 分钟前
`MYSQL`、`MYSQL_RES` 和 `MYSQL_FIELD`的含义与使用案例
数据库·mysql·c
寻月隐君1 小时前
Rust 核心概念解析:引用、借用与内部可变性
后端·rust·github
程序猿小D1 小时前
基于SpringBoot+MyBatis+MySQL+VUE实现的医疗挂号管理系统(附源码+数据库+毕业论文+答辩PPT+项目部署视频教程+项目所需软件工具)
数据库·vue.js·spring boot·mysql·毕业设计·mybatis·医疗挂号管理系统
经典19921 小时前
mysql 性能优化之Explain讲解
android·mysql·性能优化
万粉变现经纪人1 小时前
如何解决pip安装报错ModuleNotFoundError: No module named ‘django’问题
后端·python·pycharm·django·numpy·pandas·pip
ai小鬼头1 小时前
创业心态崩了?熊哥教你用缺德哲学活得更爽
前端·后端·算法
仰望星空的凡人2 小时前
【JS逆向基础】数据库之mysql
javascript·数据库·python·mysql
夕颜1112 小时前
关于 Python 的踩坑记录
后端