MySQL · 引擎特性 · InnoDB 数据页解析 (4)

数据页的checksum值的计算方法依赖参数innodb_checksum_algorithm。目前提供三种计算checksum的方法,第一种是crc校验(buf_calc_page_crc32),这种是一种比较新的计算方法,但是可以使用cpu硬件指令来加速。第二种是innodb校验,是innodb自己开发的一种计算方法,但是有新老两种变体,两种变体计算结果不同,为了兼容老的变体,需要在代码中兼容。第三种是none模式,这种计算方式不计算每个数据页的校验值,而是使用一个指定的值填充checksum字段,这种方式速度很快,但也保证不了数据的正确性。在innodb_checksum_algorithm中,除了innodb,crc32,none三种选项之外,还有strict带头的选项。strict的选项表示,在读取的时候必须是指定的校验方式的校验值才通过,其他的都不行,例如,指定了strict_crc32,那么在数据页被读取计算checksum时候,对应的校验值必须也是crc32的才可通过,但是如果指定crc32,如果存储的是innodb或者none的结果,也是可以通过校验的。之所以提供了这种选项,就是为了兼容老版本的mysql以及防止校验算法被修改而导致的数据不可用。这里提醒一下,使用strict模式由于计算量比较小,因此效率相对较高。

接下里,分析一下checksum写入和读取校验的代码细节。
在一个数据页即将被刷入磁盘的时候,会调用函数buf_flush_init_for_writing进行相关元信息的修改。这里主要包括newest_len和checksum。函数中首先往FIL_PAGE_LSN和UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM分别写入8个字节的newest_lsn,接着计算checksum,填入FIL_PAGE_SPACE_OR_CHKSUM,同时覆盖UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM起始的四个字节。我们会发现,如果算法是crc32或者none,那么前后两个checksum存储的内容相同,如果是innodb,则后面的checksum会存老版的值。
在一个数据页从磁盘读取的时候,IO线程会回调buf_page_io_complete函数,如果是读取操作,这个函数中会调用函数buf_page_is_corrupted校验数据页的正确性。buf_page_is_corrupted首先会校验头尾的newest_lsn的低四字节是否相同(FIL_PAGE_LSN+4和UNIV_PAGE_SIZE- FIL_PAGE_END_LSN_OLD_CHKSUM+4),如果不相同,直接认为数据页损坏了。接下里会把完整的8字节lsn读取出来,跟系统当前的lsn对比,如果比当前的大,也认为数据页坏了。接下里,会把首尾的checksum都读取出来,如果发现都为0,则进一步判断是否是空页,如果是空页,则认为这个数据页正常(可能是extend文件出来的空页)。接下来,计算数据页内容的校验值,与存储在数据页首尾的值进行比较。在strict算法下,必须完全一致才认为数据页完整,在非strict模式下,只要有一种值匹配即可了。如果校验值通过,则认为数据页完好。如果数据页损坏,则会调用函数buf_page_print输出数据页的信息至错误日志,这也是我们常常看到的数据页错误日志。这个函数会把数据页所有内容,space_id,page_no,头尾lsn,头尾checksum以及各种算法计算的checksum都打印出来,此外,还会依据FIL_PAGE_TYPE推测可能的数据页种类,方便排查问题。

总结

总体来说,InnoDB数据页的结构设计折中了插入,删除以及查找的效率,是一种值得学习的数据结构。此外,了解其的结构和原理,当数据页发生损坏的时候,能不慌不忙,尽最大的努力找出残存的数据,这也是一个优秀DBA不可缺少的素质。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxdxg.html