最近遇到不少的案例,其中一个比较有意思的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
网友评论