[MYSQL] mysql checksum table原理浅析并使用python实现

2024-08-09 16:01:45 浏览数 (1)

导读

前段时间, 做mysql切换的时候, 使用CHECKSUM TABLE来校验数据一致性. 源端校验时间比目标端多1倍. 而源端的innodb_buffer_pool_size恰好是目标端的1半, 于是就怀疑checksum table和buffer pool有关.

理论上应该是没得关系的, 两边配置(比如IO)等都不一样, 时间有差异也是正常的, 但就巧在这个一半上. 于是我们来稍微研究下.

使用gdb找到关键函数

虽然我们能猜到有check之类的关键词, 但这也直接使用grep -r还是太麻烦了. 我们使用一种通用的方法(之前varchar隐式转换的时候也用过)来找.

代码语言:shell复制
# 保证mysql只有一个连接

# 使用gdb打断点dispatch_command
(echo -e "break dispatch_commandncontinue"; while true;do echo 'step';done) | gdb -p `pidof mysqld` > /tmp/t20240809_dispatch_command.gdb.txt 2>&1

# mysql执行checksum table命令

等返回结果后, 我们就得到了checksum table的完整堆栈信息了. 大概1.8MB, 还是比较少的. 然后我们搜索关键词checksum就能找到对应函数了.(具体的哪行代码都有显示, 非常的方便)

mysql_checksum_table

接着我们直接打开源码(sql/sql_table.cc)分析这个函数即可

该函数比较简单, 我就直接列伪代码了. 不考虑表不存在,null等情况(这null也是一个坑...)

代码语言:c 复制
ha_checksum crc = 0;
uchar null_mask = 256 - (1 << t->s->last_null_bit_pos);
for (;;) {
	ha_checksum row_crc = 0;
	for (uint i = 0; i < t->s->fields; i  ) {
		checksum_crc32(row_crc, f->field_ptr(), f->pack_length());
	}
	crc  = row_crc;
}
protocol->store((ulonglong)crc)

也就是遍历表的每行数据, 每行数据的每个字段做crc32, 然后再将每行的crc32加起来(&(2**32-1))即可.

checksum_crc32 调用 my_checksum. my_checksum 调用crc32_z (zlib)

使用python实现checksum table

既然我们知道了原理, 那么我们就可以自己来实现checksum了. 结合ibd2sql就能快速(开并发)校验一张表的crc32值了. 但我们不整那么麻烦的. 就使用python简单模拟下即可 - _-

mysql构造数据并校验

代码语言:sql复制
create table db1.t20240809(name varchar(200) not null, url varchar(300) not null);
insert into db1.t20240809 values('ddcw','https://github.com/ddcw');
insert into db1.t20240809 values('大大刺猬','https://www.modb.pro/u/17942');
insert into db1.t20240809 values('大大刺猬','https://cloud.tencent.com/developer/user/1130242');
checksum table db1.t20240809 ;

python 构造数据并校验

代码语言:python代码运行次数:0复制
import zlib
data = [
['ddcw','https://github.com/ddcw'],
['大大刺猬','https://www.modb.pro/u/17942'],
['大大刺猬','https://cloud.tencent.com/developer/user/1130242']
]
crc32 = 0
for row in data:
	row_crc = 0
	for col in row:
		row_crc = zlib.crc32(col.encode(),row_crc)
	crc32  = row_crc
	
crc32 &= (2**32-1)
print(crc32)

和mysql的CHECKSUM TABLE校验结果是一致的, 说明我们校验方法是正确的

总结

  1. mysql的checksum table是对数据一行行校验的, 也就是和innodb_buffer_pool_size关系不大. (其实直接修改buffer_pool多再校验一次,就能发现时间是一样的, 也能说明没关系的).
  2. checksum和行的读取顺序无关(加法和顺序无关)
  3. checksum列的顺序有关.
  4. checksum和存储引擎关系不大(server层实现的)

0 人点赞