最近被问到一个问题,InnoDB表,只修改一个字段的名字,定义不修改,是否有快速方案。
这个需求的意义来源于,在表设计初期可以预留一些字段,但在预留字段投入使用时,最好能够赋予一个有意义的名字以方便使用。
复现
以下实验基于5.1.48版本。
创建一个简单表
CREATE TABLE `t` ( `c1` int(11) DEFAULT NULL, `c2` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=gbk; |
插入10w条数据。将字段c2改名为c3.
mysql> alter table t change c2 c3 int(11) default null; Query OK, 100000 rows affected (4.29 sec) Records: 100000 Duplicates: 0 Warnings: 0 |
可以看到,虽然只是简单修改了字段名,在实验机器上耗时达到4.2s,显然重做了所有数据。由于这个表没有索引,数据量也比较小,如果对于更大数据的表,则需要更长的操作时间。
分析
我们知道,在
Innodb_file_per_table参数下,每个InnoDB表有两个文件t.frm和t.ibd. 实际上,表字段信息只保存于t.frm。这个文件保存了表的定义信息,只有8k。 仅修改字段名,实际上不需要重作数据,如果能够只对t.frm做修改,则可以加快上面这个alter语句的执行速度。
源码相关
可以想象MySQL框架中应该是调用了InnoDB引擎的某个函数,用于判断是否需要重做数据。
我们追踪一下alter table语句的执行流程,在mysql_alter_table(sql_table.cc)函数中,我们看到这个局部变量need_copy_table,它有三个可能的取值。
ALTER_TABLE_METADATA_ONLY= 0,
ALTER_TABLE_DATA_CHANGED= 1,
ALTER_TABLE_INDEX_CHANGED= 2
显然这个取值的判断结果,决定了后面的执行流程。
mysql_alter_table中调用了compare_tables用于判断alter前后的表做了多大的改动,后者在这个语句中修改了need_copy_table的值。
/* Check if changes are compatible with current handler without a copy */ if (table->file->check_if_incompatible_data(create_info, changes)) { *need_copy_table= ALTER_TABLE_DATA_CHANGED; DBUG_RETURN(0); } |
这个check_if_incompatitble_table的函数实现在handler/ha_innodb.cc中。这个函数的返回值可能是COMPATIBLE_DATA_NO表示表改动前后不兼容(需要重做数据)或COMPATIBLE_DATA_YES兼容(不需要重做)。
该函数有如下片段
if (check_column_being_renamed(table, NULL)) { return COMPATIBLE_DATA_NO; } |
其实现逻辑是,如果有任何一个字段名被修改,则返回COMPATIBLE_DATA_NO。
简单修改
有了上述分析,要做到快速修改表字段名,只需要把调用check_column_being_renamed的这三行注释掉即可。重新编译发布后,执行结果如下。
mysql> alter table t change c2 c3 int(11) default null; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 |
可以看到,这回执行基本不需要时间. 对比修改前后的t.ibd的MD5,没有修改,而t.frm中只是将c2的字段名改为c3,其他不变。
问题!!
这里的问题是,这个修改,是否会有副作用?虽然我们知道修改字段名应该是对数据时没有影响的,但MySQL就是这么实现了。
更深入的调查带来了“坏消息“。 在MySQL 5.1.45版本中,就没有这个字段名的判断,也就是说在5.1.45中仅修改字段名是只修改frm文件的。
在新版本中特别加入的判断,是否有什么原因?这是撰写本文的意图。若有与此相关的bug或者文章说明,请回复或站内私信我。
延伸
实际上,关于这个问题,还可以继续深入,这里抛砖引玉。
1) 如果上述修改有副作用,副作用是什么,是否可以通过简单修改ibd文件,仍达到快速修改的目的?
2) 在例子中我们简单试验了修改字段名。对于varchar字段,alter table将字段长度增加,是否也可以按照上述思路快速修改?因为我们知道对于还没有数据的varchar字段长度改变,并不影响聚簇索引上的数据。 如果可行,对于预留varchar字段作为扩展来说,也是很有意义实现请看下篇。