源于最近有几次对于一些大表进行DDL
操作,但可能因为对InnoDB
引擎的Online DDL
的原理所知甚少,以至于有一些同学一开口就是,"执行DDL
时会锁表,如果数据量太大锁表时间太长,会阻塞到线上业务",为确保以后进行DDL
操作时能够做到心中有数,对Online DDL
做一些总结。
前身
MySQL
于5.6版本推出Online DDL
,那么在没有Online DDL
前,它是如何做DD
L?主要分为两种形式。
copy table
-
create temp table
,创建临时表结构,保持与原表结构一致 -
lock original table
,不允许对原表进行DML
操作,仅允许Queries
-
ddl on temp table
,在临时表上执行DDL (lock) -
copy original table data into temp table
,将原表数据复制到临时表 (lock) -
rename temp tablename to oringal tablename
,对原表加锁并进行rename
操作,不允许DML & Queries
,直至整个过程完成。
从整个执行过程来分析,为了保证执行期间表数据的一致性,必须通过加锁来阻止DML
操作,只允许Queries
。当表数据量过大时,在复制阶段所需要的时间过长导致整个语句在执行期间加锁阻塞其他事务的DML
语句,故而导致应用连接数撑爆,大量事务超时等问题。
inplace
inplace
又称为fast index creation
,仅支持索引的创建。
-
create frm
, 创建数据字典 -
lock original table
,不允许对原表进行DML
操作,仅允许Queries
-
copy table
,按照聚簇索引顺序读取数据,构造新的索引项,顺序插入新的索引页 -
lock original table
,锁表,不允许DML & Queries
-
rename
,替换frm
文件,完成DDL
过程
inplace
方式比起需要copy table
自然是更优的,因为只需要从聚簇索引读取新索引的列项出来构造新索引页,其他索引不受影响。但是,无论是copy table
还是 inplace
在执行操作的过程中都需要长时间锁表,阻塞DML
语句,毫无疑问对于服务运行而言简直是毁灭式的行为,因此online ddl
迫在眉睫。
在讨论online ddl
前,有必要先了解几个点,其中包括 Innodb
索引组织形式,LOCK CAUSE
,METADATA LOCK
。
索引组织形式
关于索引的官方文档
开发同学在构建数据库表时,都会根据业务特点选择合适的索引,因为索引的目的是加速搜索。从官网文档中可以看到Innodb存储引擎将索引分为Clustered Index
以及Secondary Index
,称为聚集索引与二级索引(也称聚簇索引,非聚簇索引)。
二者如何区分?Innodb只会有一个聚簇索引,聚簇索引的叶子节点上存储的是Row Data
,一般情况下是表定义中的PRIMARY KEY
,如果没有则选择表定义中第一个非空UNIQUE KEY
,如果表定义中即没有PK,也没有UK,则会通过Innodb隐藏的一个ROW ID
作为索引。而处理聚簇索引之外的其他索引,皆称为二级索引。二级索引的叶子节点上存储的是聚簇索引的值。
正因为这个特征,很多企业定义的SQL规范中都会有类似:
- PK尽量选择长整型,且趋势递增,推荐使用
AUTO INCREMENT
- PK不适合较长的字符串
- ...
LOCK CAUSE
关于LOCK CAUSE
的官方文档
默认情况下,MySQL
会选择尽可能的轻量的锁来完成DDL
操作,在某些特定情况下可以更加严格
的锁来完成操作。比如前文提到,DDL
过程中完成不允许DML,仅允许Queries
,也可以不允许DML & Queries
等。
-
LOCK=NONE
Permits concurrent queries and DML. -
LOCK=SHARED
Permits concurrent queries but blocks DML. -
LOCK=DEFAULT
Permits as much concurrency as possible (concurrent queries, DML, or both). Omitting the LOCK clause is the same as specifying LOCK=DEFAULT. -
LOCK=EXCLUSIVE
Blocks concurrent queries and DML
METADATA LOCK
关于METADATA LOCK
的官方文档
关于METADATA LOCK
的补充文档
元数据锁,是MySQL
的表锁之一,属于隐式锁(另一种是显式锁,通过lock table ... with read/write
指定)。在补充文档中可以了解到元数据锁的由来,也对其进行了解释。补充文档中提及元数据锁有11种类型,常用的有MDL_SHARED_READ
,MDL_SHARED_WRITE
及MDL_EXCLUSIVE
。元数据锁是一个跟随事务结束而释放的锁,由MySQL
控制,保护处于事务中的表元数据的一致性。
-
MDL_SHARED_READ
,Queries
时加锁,属于共享锁 -
MDL_SHARED_WRITE
,DML
时加锁,属于共享锁 -
MDL_EXCLUSIVE
,DDL
时加锁,属于独占锁
Online DDL
简述
MySQL
在5.6.7版本推了Online DDL
能力,主要是在基于原有的fast index creation
上增强实现。其次,假定前提,衡量DDL
是否足够Online
在于是否长时间允许DML
。
目前Online DDL
支持两种形式
COPY
INPLACE
MySQL
如何选择该两种形式呢?
- 支持指定,即在执行语句上指定
ALGORITHM=INPLACE / COPY
。 - 默认对于不支持
Online DDL
的sql
语句则采用COPY
,相反则采用INPLACE
。
INPLACE
会根据是否涉及到修改行记录格式分为三种情形
-
Rebuilds Table
,修改了行记录格式,比如修改列类类型、增减列等 -
Not Rebuilds Table & Not Only Modifies Metadata
,不需要重建表但是也不仅仅只是修改元数据,比如增加索引。 -
Only Modifies Metadata
,仅修改元数据,比如删除索引、设置列默认值,重命名列名等
Online DDL
支持选项
ALGORITHM={COPY|INPLACE}
-
LOCK={NONE|SHARED|DEFAULT|EXCLUSIVE}
,参照前文LOCK CAUSE
可以从官方文档查看Online DDL
支持情况。
有几个点需要特意说明
-
INPLACE
并不表示绝对支持并行DML
,但是COPY
绝对不支持并行DML
。 - 简单区分
INPLACE
与COPY
,在于是否需要创建临时表 - 简单区分
INPLACE
与COPY
,COPY
主要由Server
支持,INPLACE
主要由Innodb
支持 -
INPLACE
不是不需要额外的数据空间,取决是否为Only Modifies Metadata
-
Only Modifies Metadata
不需要Rebuilds Table
,不需要Rebuilds Table
的不一定是Only Modifies Metadata
实现原理
Online DDL
主要分为3个阶段,PREPARE
,EXECUTE
,COMMIT
- PREPARE
- 创建新的临时
frm
文件 - 持有
MDL_EXCLUSIVE
锁,禁止读写 - 根据
alter
类型,确定执行方式(copy
,rebuild
,not-rebuild
) - 更新数据字典的内存对象
- 若是需要
rebuild
,分配row_log
对象用于记录增量 - 若是需要
rebuild
,生成新的临时ibd
文件
- 创建新的临时
- EXECUTE
- 如果是仅修改元数据:
- 这部分无操作
- 其他,则是:
- 降低
MDL_EXCLUSIVE
锁,允许DML & Queries(copy 不允许写) - 记录
DDL
执行过程中产生的增量row-log
(非only modify metadata类型需要) - 扫描
old_table
的聚集索引每一条记录record
- 遍历新表的聚集索引和二级索引,逐一处理
- 根据
record
构造对应的索引项 - 将构造索引项插入
sort_buffer
块 - 将
sort_buffer
块插入新的索引 - 把
row_log
中的操作应用到新临时表中,应用到最后一个Block
- 降低
- 如果是仅修改元数据:
- COMMIT
- 升级到
MDL_EXECLUSIVE
锁,禁止读写 - 重做最后一部分的
row_log
增量 - 更新
innodb
的数据字典表 - 提交事务,写
redo
日志 - 修改统计信息
-
rename
临时的ibd
文件、frm
文件 -
DDL
完成
- 升级到
在整个
Online DDL
的过程中,并非是完全的Permits Concurrent DML
,但是由于整个过程中持有MDL_EXCLUSIVE
锁的时间较短,所以近似的认为整个过程是Permits Concurrent DML
Online DDL带来的优势
-
Online DDL
期间,Queries
和DML
操作在多数情况下可以正常执行,锁表时间大大减少。 - 允许
INPLACE
操作的DDL
,避免COPY
方式的磁盘IO及CPU资源,减少对数据库的整体负荷,使得在DDL
期间,能够维持数据库的高性能及高吞吐量; - 允许
INPLACE
操作的DDL
,比需要COPY
到临时表的操作要更少占用buffer pool
,避免以往DDL
过程中性能的临时下降,因为需要拷贝数据到临时表,这个过程会占用到buffer pool
,导致内存中的部分频繁访问的数据会被清理出去。
资料
https://www.cnblogs.com/cchust/p/4639397.html
https://www.cnblogs.com/xinysu/p/6732646.html
https://www.cnblogs.com/dbabd/p/10381942.html
http://mysql.taobao.org/monthly/2021/03/06/
网友评论