我前段时间发现并解决的一个innodb和xtradb的bug,这个我在MariaDB官网上面提交的bug报告:https://jira.MariaDB.org/browse/MDEV-9581
这个bug页面里面有我写的详细的重现流程和错误分析以及patch,以及我与MariaDB的开发者就这个bug进行的一些技术细节的讨论,还有这个bug后来的状态更新。
这里用中文再简述一下症状及其分析,以及解决方法。
症状是MariaDB-10.1.9无法使用MariaDB-10.0.10产生的数据目录:当指定datadir为这样一个目录时,mysqld进程在启动过程中发现错误而退出了。mysqld.err里错误信息是这样的:
InnoDB: Error: Current page size 4096 != page size on page 16384
2016-02-16 17:20:39 140521425086336 [ERROR] InnoDB: innodb-page-size mismatch in data file /my/data/dir/ibdata1
2016-02-16 17:20:39 140521425086336 [ERROR] InnoDB: Could not open or create the system tablespace. If you tried to add new data files to the system tablespace, and it failed here, you should now edit innodb_data_file_path in my.cnf back to what it was, and remove the new ibdata files InnoDB created in this failed attempt. InnoDB only wrote those files full of zeros, but did not yet use them in any way. But be careful: do not remove old data files which contain your precious data!
2016-02-16 17:20:39 140521425086336 [ERROR] Plugin 'InnoDB' init function returned error.
2016-02-16 17:20:39 140521425086336 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.
2016-02-16 17:20:39 140521425086336 [Note] Plugin 'FEEDBACK' is disabled.
2016-02-16 17:20:39 140521425086336 [ERROR] Unknown/unsupported storage engine: innodb
2016-02-16 17:20:39 140521425086336 [ERROR] Aborting
也就是说,如果你之前在使用MariaDB-10.0.x,有一个数据目录,里面有你珍贵的数据,然后现在你打算使用MariaDB-10.1.x,你会痛苦地发现,MariaDB-10.1.x无法用原来的数据目录启动,也就无法使用mysql_upgrade来升级数据目录。当然,规避的办法是使用dump/load,但愿你的数据量不会太大。
经过调试我发现问题是这样造成的:
在MariaDB-10.1.x中,MariaDB的开发者给表空间文件头部的标志字段新增了三个标志位(每个标志位未必只占用一位) PAGE_COMPRESSION, PAGE_COMPRESSION_LEVEL, ATOMIC_WRITES,但是这三个标志位并不是被追加到已有标志位后面的,而是被插队到了PAGE_SSIZE这个标志位的前面,导致以及它后面的标志位在MariaDB-10.1.x中被推到了该标识字段更加高位的位置,于是MariaDB-10.1.x使用老版本的数据文件的时候,会从第13位开始的4个位作为page size, 但是实际上在老版本的数据文件中,表空间文件头的标志字段只有11位有效,更高位都是0(这个PAGE_SSIZE是在第6,7,8,9位)。于是MariaDB-10.1会读到page size是0,而这里的0被innodb 认为是默认的页面大小,也就是16K,而配置文件中我们制定的是4K,于是发生了上面的错误。
我的解决方法很简单: 把新增的标志位放到原有的所有标志位之后(更高位),这样MariaDB-10.1.x就会从正确的位置去读取老版本中有的那些标志位了,同时新增的标志位也不会因为更改了位置受到影响,对我们来说没有mariadb-10.1.x的旧数据目录需要升级。这样做是正确的,可以通过所有的MariaDB自带测试,并且也可以让MariaDB-10.1.x正确地使用老版本的数据文件来启动。当然,启动之后要首先运行mysql_upgrade来更新一些系统表。
MariaDB官方的解决方法需要更加复杂些,因为MariaDB还要合并MySQL的功能,而MySQL-5.7.12在这个表空间标志字段也新增了3个不同的标志位SHARED,TEMPORARY,ENCRYPTION,于是他们的工程师告诉我说为了彻底避免现在和将来与MySQL的冲突,决定把自己在MariaDB-10.1.x新增的 PAGE_COMPRESSION, PAGE_COMPRESSION_LEVEL, ATOMIC_WRITES这三个标志位放到一个Innodb专属的元数据表中,同时为了帮助现在使用MariaDB-10.1.x的用户升级表空间文件,他们还需要开发一个工具把新增的标志位的值存储到这个系统表中,这个工具很可能直接做到mysql_upgrade程序里面即可。 当未来我们需要升级到更高的mariadb版本,比如10.2.10的时候,我们也只需要修改这个升级工具即可让用户轻松升级数据文件。到目前为止,MariaDB官方还没有修复这个bug。