instant秒级加列
背景
通常情况下大表的DDL操作都会对业务产生很大的影响,需要在业务低峰期做。MySQL 5.7支持原生DDL工具Copy和Inplace算法、以及开源DDL工具gh-ost,减少了DDL期间DML操作被阻塞的情况。但是大表DDL仍然需要花费很长时间。
instant秒级加列算法,让添加列的时候不再需要rebuild整个表,只需要在表的metadata中记录新增列的基本信息即可,可以很快执行完成。但是目前支持的DDL操作有限。
语法
在Alter语句后面增加“ALGORITHM=INSTANT”即代表使用instant算法,例如:
ALTER TABLE *tbl_name* ADD COLUMN *column_name* *column_definition*, ALGORITHM=INSTANT;
使用限制
使用场景的限制:
- 部分场景下增加、删除、重命名(MySQL 8.0.28之后)列。
- 设置或删除列的默认值。
- 修改ENUM或SET列的定义。
- 更改索引的类型(BTREE | HASH)。
- 增加或删除虚拟列。
- 表名重命名。
添加或删除列的限制:
- 不支持有其他INSTANT语句在同一行的操作在同一条语句的情况。
- 新增列将会放到最后,不支持改变列的顺序(MySQL 8.0.29后支持任意位置加列)。
- 不支持在行格式为COMPRESSED的表上快速加列或删除。
- 不支持在已经有全文索引的表上快速加列或删除。
- 不支持在临时表上快速加列或删除。
重命名列的限制:
- 不支持重命名被其他表引用的列。
- 不支持重命名列的操作与生成或者删除虚拟列在同一个语句中。
修改ENUM或SET列的限制:
- 不支持ENUM或者SET列数据类型占用的存储空间发生变化。
增加或删除虚拟列的限制:
- 不支持对分区表的增加或删除操作。
新的数据字典信息
在执行instant add column的过程中,MySQL会将第一次intant add column之前的字段个数以及每次加的列的默认值保存在tables系统表的“se_private_data”字段中。
- dd::Table::se_private_data::instant_col:第一次instant add column之前表上的列的个数。
- dd::Column::se_private_data::default_null:标识instant column的默认值是否为NULL。
- dd::Column::se_private_data::default:当instant column的默认值不是NULL时存储具体的默认值,column default value需要从InnoDB类型byte转换成“se_private_data”中的char类型。
载入数据字典
MySQL从系统表读取表定义时,会将instant column相关的信息载入到InnoDB的表对象“dict_table_t”和索引对象“dict_index_t”中。
- dict_table_t::n_instant_cols:第一次instant add column之前的非虚拟字段个数(包含系统列)。
- dict_index_t::instant_cols:用于标示是否存在Instant column。
- dict_index_t::n_instant_nullable:第一次instant add column之前的可为NULL的字段个数。
- dict_col_t::instant_default:存储instant列默认值及其长度。
记录格式
为了支持instant add column,针对COMPACT和DYNAMIC类型引入了新的记录格式,主要为了记录字段的个数信息。
- 如果没有执行过instant add column操作,则表的行记录格式保持不变。
- 如果执行过instant add column操作,则所有新的记录都会设置一个特殊的标记,同时在记录内存储字段的个数。
INSTANT_FLAG使用了info bits中的一个bit位,如果记录是第一次instant add column之后插入的,该flag被设置为1。
查询
查询的流程没有变化,但对于没有存储在记录中的instant column,直接填默认值即可。
插入
执行instant add column后,旧数据的格式没有变化,新插入的数据按照新格式存储。新记录的info bits中的一个位被设置成了REC_INFO_INSTANT_FLAG,表示这个记录是instant add column之后创建的。