innodhecksum[1]是MySQL自带的offline工具bin,可检查innodb数据文件是否损坏。使用方式如下。
shell> bin/innochecksum [options] file_name
MySQL的系统表空间(ibdata)、用户表空间(*.ibd)、redo log(ib_logfile)等文件,结构类似,都由page构成,UNIV_PAGE_SIZE默认16KB,定义如下。了解更多可参考文章[2]或者innodb-java-reader工具[3]。
#define UNIV_PAGE_SIZE (2 * 8192)
如下图所示,page前后是38字节的header和8字节的trailer。checksum校验算法会利用index4-26以及index38-16376这两部分计算。详细实现参考ut0crc32.cc[4]。图片一部分参考链接[5]
page的checksum算法有三种,参考官方文档[6]。
innodb
crc32
none
从MySQL 5.7.7版本之后crc32是默认的checksum算法。
crc32的校验算法有两种实现,如果CPU支持SIMD SSE4_2指令集,则硬件加速,直接inline汇编代码;如果不支持,fallback到软件算法实现。
innochecksum执行的逻辑很简单。
while (!feof(fil_in)) {
bytes = read_file //read 16KB
is_page_corrupted //using crc32/innodb/none algorithm
}
优化innochecksum前,先看看基准性能如何。环境如下。CPU: Intel Xeon CPU E5-2682 v4 @ 2.50GHz(开启超线程,逻辑核数64)
内存: 256G
NVMe SSD硬盘:PCIe 3.0 x4, NVMe, Intel® SSD DC P3600 Series, 1.6TB
背面图如下,可以看到排列整齐的MLC NAND闪存颗粒[8]。
规格如下。官方资料,顺序写1600 MB/s,顺序读2600 MB/s。
测试数据文件是TPC-H的LINEITEM,大小有72G左右。(dbgen -s 70生成后导入MySQL,文件较大,为了更好的观测)。
直接上图,MySQL自带的bin执行innochecksum -C crc32 file_name,带宽只有642 MB/s,远低于这块盘的顺序读吞吐限。
优化后的最优版本可以达到2503 MB/s,非常接近官方最高吞吐。
下面第一部分 测试NVMe SSD读写极限吞吐,第二部分 阐述优化实现方案。
1. 测试NVMe SSD读写极限吞吐
fio[9]测试读写吞吐,ioengine选择libaio。
fio写:
./fio --filename=test -iodepth=16 -ioengine=libaio -direct=1 -rw=write -bs=1m -size=512g -numjobs=8 -runtime=50 -group_reporting -name=test-write
write: bw=1543.2MB/s, iops=1543
fio读:
./fio --filename=test -iodepth=16 -ioengine=libaio -direct=1 -rw=read -bs=2m -size=512g -numjobs=8 -runtime=50 -group_reporting -name=test-read
read : bw=2382.8MB/s, iops=2382
测试结果基本符合官方的数据,但都差一些,所以手写代码再压测下,部分代码参考文章[10]。源码链接如下。https://github.com/neoremind/io_benchmarkgithub.com
测试buffer io write-back写,代码如下。
#include
#include
#include
#i