MySQL:8028(含)以下 checksum table 不一致的问题

2024年 6月 12日 60.8k 0

作者简介:高鹏,笔名八怪。《深入理解MySQL主从原理》图书作者,同时运营个人公众号“MySQL学习”,持续分享遇到的有趣case以及代码解析!

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

触发方式

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

<code>mysql&gt; create table i1(id int);
Query OK, 0 rows affected (0.07 sec)

mysql&gt; alter table i1 add name varchar(20);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql&gt; create table i2 like i1;
Query OK, 0 rows affected (0.04 sec)

mysql&gt; insert into i1 values(1,'g');
Query OK, 1 row affected (0.00 sec)

mysql&gt; insert into i2 values(1,'g');
Query OK, 1 row affected (0.01 sec)

mysql&gt; checksum table i1;
+-------------+------------+
| Table       | Checksum   |
+-------------+------------+
| test1227.i1 | 1843999801 |
+-------------+------------+
1 row in set (0.00 sec)

mysql&gt; checksum table i2;
+-------------+------------+
| Table       | Checksum   |
+-------------+------------+
| test1227.i2 | 1025811850 |
+-------------+------------+
1 row in set (0.00 sec)

问题解析

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

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

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

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

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

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

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

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

<code>mysql&gt; 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函数的末尾),如下

MySQL:8028(含)以下 checksum table 不一致的问题-1

简单修改

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

MySQL:8028(含)以下 checksum table 不一致的问题-2

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

MySQL:8028(含)以下 checksum table 不一致的问题-3

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论