前一篇文章末尾提到InnoDB快速修改字段长度。其实用场景在于,在设计表时,若需要预留varchar类型字段,还无法确定实际需要的长度。而当需要启用到预留的字段时,表中可能已经有很多数据,此时要根据需要修改字段长度, 若能够不需要重做数据,则能够减少这个修改操作对线上服务的影响。
几点说明
1、 注意到这里适用的是varchar类型, char类型不在本文讨论范围内。实际上,由于varchar类型字段数据并不是直接存储在聚簇索引中,才使得快速修改成为可能。而char类型改变长度至少要将整个聚簇索引重做,因此不能做到“不修改数据”
2、 与前一篇文章的思路类似,我们的目的是在执行alter table语句的时候,只修改frm文件。
3、 当然实际执行alter table修改字段长度的时候,考虑到字段中可能已经有数据,因此若是长度定义变小,则必须重做数据,因为超过长度的数据要作截断,否则逻辑上就不通过了。 因此这里只适用于将长度改大的情况。 从我们的需求出发点来看,这一点问题并不大,在预留的时候设置“小一些”即可。(小是相对的,后面会说到)
4、 我们用到的语句形如 alter table t c c varchar(300) default null. 而c字段原来声明为varchar(290) default null.
源码分析
从前一篇文章中我们知道MySQL在compare_tables这个函数中判断当前执行的alter table语句是否需要重做数据。
在这个函数中有这么一段
/*
Go through fields and check if the original ones are compatible
with new table.
*/
for()
{
…
if (!(tmp= field->is_equal(tmp_new_field)))
{
*need_copy_table= ALTER_TABLE_DATA_CHANGED;
DBUG_RETURN(0);
}
…
}
|
for循环中对每个字段的修改作了判断,其中field->is_equal就用于判断修改前后的字段定义是否完全相同。这里field是一个基类对象,通过多态调用Field_varstring::is_equal (sql/field.cc).
uint Field_varstring::is_equal(Create_field *new_field)
{
if (new_field->sql_type == real_type() &&
new_field->charset == field_charset)
{
if (new_field->length == max_display_length())
return IS_EQUAL_YES;
if (new_field->length > max_display_length() &&
((new_field->length <= 255 && max_display_length() <= 255) ||
(new_field->length > 255 && max_display_length() > 255)))
return IS_EQUAL_PACK_LENGTH; // VARCHAR, longer variable length
}return IS_EQUAL_NO;
}
|
这个函数的逻辑比较简单。 real_type()返回的是当前的字段类型(当然是varcahr), field_charset是当前字段实用的字符集, max_display_length()返回当前定义的长度。
我们看到这个函数有三种返回值, 其中IS_EQUAL_PACK_LENGTH类似我们需要的情况,为什么说类似呢, 这个判断中要求的不仅仅是长度增大,还要求修改前后的长度定义,要么都小于255,要么都大于255。 这个深层原因给我们带来一点麻烦,后面再说。
IS_EQUAL_PACK_LENGTH这个返回值,说明框架层考虑到这种情况是可以特殊处理的,而遗憾的是InnoDB源码中没有利用这个值,我们就用这个返回值来修改一下InnoDB中的判断逻辑。
简单修改
为了不影响其他引擎的结果,我们只在InnoDB内部修改。我们知道check_if_incompatible_data这个函数的返回值,决定了MySQL是否重做表数据。
bool
ha_innobase::check_if_incompatible_data(
HA_CREATE_INFO* info,
uint table_changes)
{
if (table_changes != IS_EQUAL_YES) {
return(COMPATIBLE_DATA_NO);
}
…
}
|
这个传入的table_changes, 是前面各种判断的异或结果(因此在我们的例句中,这里的值是IS_EQUAL_YES|IS_EQUAL_PACK_LENGTH)。
这里判断逻辑要求必须是 IS_EQUAL_YES. 按照我们的分析,修改成如下
bool
ha_innobase::check_if_incompatible_data(
HA_CREATE_INFO* info,
uint table_changes)
{
if ((table_changes == IS_EQUAL_NO) ||
(table_changes & ~(IS_EQUAL_YES|IS_EQUAL_PACK_LENGTH) != 0)) return(COMPATIBLE_DATA_NO);
}
…
}
|
说明:虽然目前只有三种返回值,但从逻辑严谨出发,还是要判断table_changes是否在(IS_EQUAL_YES|IS_EQUAL_PACK_LENGTH)所表示的位标识范围内。
重新编译发布后,执行结果如下。
mysql> alter table t c c varchar(300) default null;
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0
|
可以看到,这回执行基本不需要时间。
遗留问题
细心读者一定发现我们的例子故意绕过了上面说到的255的问题,实际上如果原来定义为varchar(4), 再修改为varchar(300),按照我们的实现,还是需要重做数据的。
判断逻辑可以很简单的修改,问题是,MySQL为什么要作这个255的分界判断?
实际上,varchar字段的实际内容前有1个或2个字节表示实际内容的长度,而到底是1个字节还是2个字节,就取决于创建表或修改的时候,这个字段声明的长度。也就是说,varchar(4) 字段的实际内容前,用1个字节表示实际长度,而varchar(300)的实际内容前,用2个字节。
因此,如果只是在修改前后长度在255两侧,则必须重做数据。
这样造成的问题是,从我们的需求出发,要预留字段时候,就必须先估计预留的字段大概的长度(是否超过255)。
可以将所有的varcahr字段都预留超过255字节,问题并不大,只是增加了1个字节空间而已。
遗留问题的解决方案
当然这事儿也不是不能解决的,InnoDB是为了节省空间,如果我们放弃这个节省策略,对于所有的varchar,都用2个字节来保存实际长度,就没这个问题了。下篇再续。
再次呼唤,本文所作修改目前只作了简单的回归测试。还没有完全确认是否引入副作用,若有相关文章涉及与此相关,请回复或站内私信我。
分享到:
相关推荐
MySQL报警,从库的数据库挂了,一直在不停的重启,打开错误日志,发现有张表坏了。innodb表损坏不能通过repair table 等修复myisam的命令操作。
从案例看InnoDB表设计优化
这篇文章主要介绍了MySQL索引长度限制原理解析,文中通过示例代码...varchar的最大长度是指字符长度,若数据库字符集为utf-8,则一个字符占3个bytes。因此在utf-8字符集下,innodb引擎创建的单列索引长度不能超过255个
MySql innodb 引擎表存储分析
mysql innodb类型数据库表 根据ibd文件获取表 space id,用于恢复innodb类型数据表数据
MySQL 的企业级解决方案,高实用性以及强健的数据完整性 MySQL 事务,行级锁定,热备份以及外键支持 - - 无需损失 MySQL 的高速性能 InnoDB 给 MySQL 提供了具有事务(commit)、回滚(rollback)和崩溃修复能力(crash ...
对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。 ◆4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。 ◆5.LOAD ...
可以有多个数据文件,如果没有设置innodb_file_per_table的话, 那些Innodb存储类型的表的数据都放在这个共享表空间中,而系统变量innodb_file_per_table=1的话,那么InnoDB存储引擎类型的表就会产生一个独立表空间...
从物理意义上来讲,InnoDB表由共享表空间文件(ibdata1)、独占表空间文件(ibd)、表结构文件(.frm)、以及日志文件(redo文件等)组成。 1、表结构文件 在MYSQL中建立任何一张数据表,在其数据目录对应的数据库...
本文档包括的是mysql 5.6版的innodb的系统表的详细描述
innodb表损坏不能通过repair table 等修复myisam的命令操作。现在记录下解决过程,下次遇到就不会这么手忙脚乱了。 处理过程: 一遇到报警之后,直接打开错误日志,里面的信息:InnoDB: Database page corruption ...
Mysql innodb tablespace 表空间实践
MySQL的体系结构及InnoDB表引擎的配置.pdf
如果用户使用InnoDB存储引擎建立表的时候,没有指定主键,则Mysql会自动的帮你找到一个合适的唯一索引作为主键,若找不到符合条件唯一索引条件的字段时,会生成类似于ROW_ID的虚拟列充当该InnoDB表的主键;...
mysql 5.6 新特性 innodb
BIT数据类型保存位字段值,并且支持MyISAM、MEMORY、InnoDB和BDB表。 作为SQL标准的扩展,MySQL也支持整数类型TINYINT、MEDIUMINT和BIGINT。 表的字段类型 数值类型 表的字段类型 日期和时间类型 表示时间值的日期和...
这是我从网上找到的mysql/mariadb对innodb表进行数据恢复的工具,实现从innodb的数据库文件中恢复数据,用于实现下面情况:1、直接下载了innodb数据库的文件,而不是导出其数据,想恢复数据时(需要有完整的文件,...
最近在学习MySQL技术内幕 InnoDB存储引擎 第2版,整理了一些文档分享出来,同时也方便以后查看。若有不当之处,烦请批评指正。 1. MySQL体系结构和存储引擎 2. InnoDB存储引擎 2.1 InnoDB体系结构 2.2 ...
最近在优化mysql innodb存储引擎,把共享表空间转换成独立表空间,下面是详细步骤