`
丁林.tb
  • 浏览: 797231 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

InnoDB表快速修改varchar字段长度方案

阅读更多

前一篇文章末尾提到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.

 

源码分析

从前一篇文章中我们知道MySQLcompare_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个字节来保存实际长度,就没这个问题了。下篇再续。

 

再次呼唤,本文所作修改目前只作了简单的回归测试。还没有完全确认是否引入副作用,若有相关文章涉及与此相关,请回复或站内私信我。

 

5
1
分享到:
评论

相关推荐

    行业-25 对于VARCHAR这种变长字段,在磁盘上到底是如何存储的.rar

    - 空间效率:如果一个表中的VARCHAR字段经常存储短字符串,那么这种方法可以避免大量浪费的空间。 - I/O效率:通过减少单个数据行的大小,可以提高磁盘I/O效率,因为较小的行更容易缓存,并且在读写时需要处理的...

    mysql VARCHAR的最大长度到底是多少

    然而,这65,535字节的限制并非VARCHAR字段能够实际存储的最大字符数,因为它还需要考虑到行的总长度限制以及数据页的结构。在InnoDB存储引擎中,每行数据的总长度不能超过65,535字节,包括所有列的值。这包括NULL...

    创建图书表book,保存图书的信息,要求使用InnoDB引擎存储

    在实验过程中,除了基本的创建、修改和删除表的操作,还涉及到了对字段的增删改查,如添加新字段、重命名字段、更改字段数据类型以及删除字段。这些操作对于理解和管理MySQL数据库非常重要,对于初学者来说是很好的...

    MySQL数据类型varchar详解

    首先,减去已知的字段长度和长度标识位,再减去NULL标识位的总字节数,即可得到`VARCHAR(N)`的最大字节数,然后根据字符集确定字符数。 总之,`VARCHAR`在MySQL中的使用需要综合考虑字符集、存储引擎、字段数量以及...

    mysql创建、删除、修改表

    ### MySQL 创建、删除、修改表的关键知识点 #### 一、MySQL概述 MySQL是一种关系型数据库管理系统,被广泛应用于各种规模的应用程序中。本篇将详细介绍MySQL中如何进行表的创建、删除与修改操作。 #### 二、创建...

    Mysql 报Row size too large 65535 的原因及解决方法

    当表中的VARCHAR字段过多,或者单个VARCHAR字段设置的长度远超过实际需要,就可能导致行大小超出限制。例如,如果你有很多VARCHAR(300)字段,即使实际存储的数据很少,但由于每个字段都有一个字节的长度指示器,这些...

    MySQL大表性能优化方案 和 MySQL高性能表设计规范

    3. **合适字段长度**:字段长度应根据实际需求设定,避免过长导致内存和存储浪费。 4. **自增主键**:使用整数类型的自增主键,有利于索引效率和数据插入。 5. **避免NULL**:尽量避免使用NULL,NULL值会增加索引...

    MySQL夺命连环问面试题分享给需要的同学.docx

    4. 选择合适的字段长度 5. 优先考虑逻辑删除,而不是物理删除 6. 每个表都需要添加这几个通用字段如主键、create_time、modifed_time等 7. 一张表的字段不宜过多 8. 尽可能使用not null定义字段 9. 设计表时,评估...

    php数据库增删改查,用户管理有关问题20161206(1).txt

    需要注意的是,这里所有字段的长度都设置为10,这可能不够合理,实际应用中应根据实际情况调整字段长度。 ### 三、修改表结构 #### 1. 重命名表 当需要改变表的名字时,可以使用`RENAME TABLE`语句: ```sql ...

    mysql中插入多text类型问题

    * Dynamic 格式:这种格式适合动态长度字段,如 varchar 等。它可以使得数据被高效地存储,并且可以减少溢出页的数量。 结论 通过修改 MySQL 的配置文件和表的行格式,我们可以解决 MySQL 中插入多个 text 类型...

    38.都说InnoDB好那还要不要使用Memory引擎?1

    然而,Memory引擎不支持Blob和Text字段,且对于varchar类型的处理类似于char,即作为固定长度字符串存储,这可能导致内存利用率不高。 为了解决Memory引擎的范围查询问题,可以为表创建B-Tree索引。例如,在表t1的...

    delphi 建表处理工具,实现建表功能,字段说明等信息的创建。

    每个字段需要指定数据类型(如VARCHAR、INT、DATE等)、长度、是否可为空、默认值等属性。在Delphi中,你可以使用TADOCommand或TSQLQuery组件来执行这些SQL语句。 字段说明信息的创建涉及元数据管理。你可以创建一...

    西工大数据库实验1-2020

    - 调整SNO字段长度:`ALTER TABLE S MODIFY COLUMN SNO VARCHAR(20);` 9. **存储引擎对比** - 使用InnoDB和MyISAM创建表,观察物理存储文件差异。InnoDB支持事务处理,数据以行式存储,有外键支持;MyISAM不支持...

    MySQL中VARCHAR与CHAR格式数据的区别

    2. 空间效率:如果字段长度变化较大,如地址,选择VARCHAR可以节省存储空间。 3. 性能要求:如果对查询速度有较高要求,可以优先考虑CHAR。 4. 存储引擎:MyISAM存储引擎通常建议使用固定长度的列,而InnoDB则更适合...

    MySQL数据库char与varchar的区别分析及使用建议

    1. **字段长度**:如果字段的值通常接近固定长度,且变化不大,`CHAR`可能是更好的选择,因为它避免了因长度变化带来的额外操作。例如,身份证号或邮政编码这类长度固定的字段。 2. **空间效率**:如果字段的值长度...

Global site tag (gtag.js) - Google Analytics