美文网首页
MySQL:8028(含)以下 checksum table 不

MySQL:8028(含)以下 checksum table 不

作者: 重庆八怪 | 来源:发表于2024-01-22 09:53 被阅读0次

    最近遇到不少的案例,其中一个比较有意思的checksum table在8028(含)以下出现instant列会导致checksum table不一致的问题,因为日常校验数据的时候经常使用checksum table进行校验,感觉问题比较严重,就稍微看了下,但是也没特别仔细的学习,现记录如下。

    触发方式

    这个触发方式很简单(8023/8028测试),如下:

    mysql> create table i1(id int);
    Query OK, 0 rows affected (0.07 sec)
    
    mysql> alter table i1 add name varchar(20);
    Query OK, 0 rows affected (0.06 sec)
    Records: 0  Duplicates: 0  Warnings: 0
    
    mysql> create table i2 like i1;
    Query OK, 0 rows affected (0.04 sec)
    
    mysql> insert into i1 values(1,'g');
    Query OK, 1 row affected (0.00 sec)
    
    mysql> insert into i2 values(1,'g');
    Query OK, 1 row affected (0.01 sec)
    
    mysql> checksum table i1;
    +-------------+------------+
    | Table       | Checksum   |
    +-------------+------------+
    | test1227.i1 | 1843999801 |
    +-------------+------------+
    1 row in set (0.00 sec)
    
    mysql> checksum table i2;
    +-------------+------------+
    | Table       | Checksum   |
    +-------------+------------+
    | test1227.i2 | 1025811850 |
    +-------------+------------+
    1 row in set (0.00 sec)
    

    问题解析

    那么这个问题肯定首先需要在checksum table的代码中找到为什么计算的值不一样(mysql_checksum_table函数),其中分析字段的返回值后发现并没有什么问题。但是在分析null_bytes的时候发现了其值就是计算checksum table不同的根源如下:

    t->record[0][t->s->null_bytes - 1] |= null_mask; //A:null_mask有差异
                  if (!(t->s->db_create_options & HA_OPTION_PACK_RECORD))//B:HA_OPTION_PACK_RECORD标记有差异
                    t->record[0][0] |= 1;
    

    而A中null_mask的差异也源自于HA_OPTION_PACK_RECORD是否设置了标记,因此A和B的差异都源于表的属性HA_OPTION_PACK_RECORD是否设置。
    这一位的标记实际上就是对于varchar这种可变字段而言这个表是应该有这种属性。对于这一位的标记而言,其来源实际上读取的数据字典。

    那么接下来就需要分析为什么时候字典会设置这一位标记,获取字典信息如下,

      table_options.get("pack_record", &bool_opt); //读取mysql.tables表获取信息
      if (bool_opt) share->db_create_options |= HA_OPTION_PACK_RECORD;
    

    从建表信息中获取,字典设置属性(fill_dd_table_from_create_info):

    table_options->set("pack_record",
                         create_info->table_options & HA_OPTION_PACK_RECORD);
    

    而从debug版本的字典来看,确实这一个标记是不同的,如下,

    mysql> select options from mysql.tables where name in ('i1','i2');
    +--------------------------------------------------------------------------------------------------------------------------+
    | options                                                                                                                  |
    +--------------------------------------------------------------------------------------------------------------------------+
    | avg_row_length=0;encrypt_type=N;key_block_size=0;keys_disabled=0;pack_record=0;stats_auto_recalc=0;stats_sample_pages=0; |
    | avg_row_length=0;encrypt_type=N;key_block_size=0;keys_disabled=0;pack_record=1;stats_auto_recalc=0;stats_sample_pages=0; |
    +--------------------------------------------------------------------------------------------------------------------------+
    2 rows in set (0.01 sec)
    

    接着分析DDL的commit流程的时候发现,首先根据 add字段新建出来一个新的表结构,然后在提交的时候从老表的结构里面copy的现有字段的一些数据到新的表结构,这个过程新表的新加字段本来是varchar 应该是 pack_record=1,但是在拷贝options的时候先做了clear操作清空了,然后将老表的options拷贝过去,这样实际上pack recorid属性丢失了,然后存入数据字典。那么字典里面也会丢失(dd_copy_private函数的末尾),如下


    2ab6d5463a207e9f8cee2f128ba04e4.png

    简单修改

    修改就是先增加一个临时的变量,将新表的pack_record=1记录一下,待最后对丢失的pack reorid属性设置一下就可以了。如下,


    673e6b29144d58688cba73fb40d7a5d.png

    这样修改checksum功能正常了,如下,


    image.png

    相关文章

      网友评论

          本文标题:MySQL:8028(含)以下 checksum table 不

          本文链接:https://www.haomeiwen.com/subject/swjyodtx.html