一、问题模拟
使用5.7.22版本:
本DDL语句主要完成:
- 扩展varchar从24*4到30*4
- 更改字段的comment
按照常理来讲这个DDL是只修改元数据的,因此应该瞬间完成,但是实际在5.7.22版本中这个语句重建了索引a,耗时如下:
很明显重建了索引,才会有这么高的耗时。而在5.7的新版本或者8.0中测试这个语句是瞬间完成的。
二、官方文档说明
也就是说正常的扩展varchar的长度,只要字符集字节数量*字符数量不跨越256,那么就是修改元数据,不会重建索引。
三、问题分析
既然不符合官方文档的说明,那么这个问题肯定是某种BUG导致。当我们进行DDL操作的时候,需要对比更改部分和现有的数据字典中标定义的差别,然后根据这些差别来定义操作方式,然后根据操作方式来判断哪一种DDL 方式比较合适,关于定义操作方式的部分来自于函数fill_alter_inplace_info,而在函数中会根据新表的索引和老表的索引字段的长度判断是否需要drop索引和新建索引,代码中体现为如下:
而加入到modify buffer后这个索引就是需要drop并且add的,因此DDL类型定义为,Alter_inplace_info::DROP_INDEX|Alter_inplace_info::ADD_INDEX,因此就需要进行索引的删除和重建,因此关键就是函数has_index_def_changed的更改,我们先看5.7.22的这个BUG相关的判断点:
也就是当索引字段长度更改了就返回true。而在新版本中:
变更还是比较大的,主要是key_part->field->is_equal((Create_field *)new_field) == IS_EQUAL_PACK_LENGTH)这个条件是否满足,而判定的函数为Field_varstring::is_equal,
其重点为如下:
- A:如果新的字段长度>老的字段的长度
- B:字段长度不能跨越255字节
那么则返回IS_EQUAL_PACK_LENGTH,因此就这个点上has_index_def_changed函数就会返回false,不会删除和重建索引了。
四、相关BUG
这个BUG虽然有点老了,是5.7.23修复的,如下:
但是对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障,因此还是建议任何DDL操作除了翻看官方文档以外,都需要在相同版本的数据库测试环境测试其耗时是否达到预估水平。