MySQL 8.4.13:丢弃表空间再导入旧镜像后,点查为什么读到旧数据
适用版本:MySQL 8.4.13(2026-10-20)及之后。8.4.13 修复了两个与 Adaptive Hash Index(AHI)和表空间导入相关的缺陷,它们会影响「导出表 → 修改表 → DISCARD TABLESPACE → 导入旧镜像」这类操作后的读一致性与崩溃恢复。
现象一:点查返回被丢弃代的行或值
操作序列是:导出一张表,之后表被修改,其表空间被 DISCARD,再导入一份更早的导出镜像。此时点查(point lookup)可能返回被丢弃的那一代(generation)表空间中的行或值,而不是当前导入镜像里的内容。
原因是 AHI 中残留了指向旧表空间的条目。点查命中 AHI 时直接返回,绕过了 B-tree 上的实际页。8.4.13 起,陈旧的 AHI 条目被当作 miss 处理,查询回退到正常的 B-tree 遍历,返回值以实际页为准。
Bug #39661793
现象二:IMPORT TABLESPACE 失败后,eviction 阶段断言崩溃
ALTER TABLE ... IMPORT TABLESPACE 失败后,被丢弃表空间的残留页可能在后续页面淘汰(eviction)时触发断言:
[ERROR] [MY-013183] [InnoDB] Assertion failure: btr0sea.cc:1082:block->page.id.space() == index->space
失败导入的清理逻辑现在会保留移除陈旧 AHI 条目所需的 index space identifier,使清理阶段能定位并删除这些条目。
Bug #39661792
排查步骤
1. 确认版本与 AHI 开关
SELECT VERSION();
SHOW GLOBAL VARIABLES LIKE 'innodb_adaptive_hash_index';
MySQL 8.4.0 起 innodb_adaptive_hash_index 默认是 OFF(8.0 中默认 ON)。若实例上显式打开了它,先确认是否属于本次现象的影响范围;关闭后观察点查返回值是否恢复正常。
2. 用 EXPLAIN 确认访问路径
EXPLAIN SELECT ... WHERE pk = ?;
EXPLAIN 只能反映优化器选择的访问路径与索引使用情况,AHI 对优化器不可见,因此看不到 AHI 命中。它的作用是排除「查询本身走了错误的索引或覆盖索引」这一层原因,把问题定位到存储层。
3. 重建表或索引
重建会生成新的索引结构,使旧的 AHI 条目失效:
ALTER TABLE t FORCE;
-- 或
OPTIMIZE TABLE t;
-- 或对相关索引 DROP 后重新 ADD
重建后再执行原来的点查,对比返回值。
4. 导入前做一致性检查
.cfg元数据文件必须与.ibd匹配,两者来自同一次FLUSH TABLES ... FOR EXPORT导出。- 导出完成后不要再修改该表;修改过的表不要与旧镜像混用。
IMPORT TABLESPACE执行期间表处于锁定状态,不能并发写入。- 导入失败后不要直接复用同一份镜像重试,先确认残留状态已被清理,并升级到包含上述修复的版本。
同版本的其他 InnoDB 修复
- InnoDB redo-log consumer UDF 传入 null 输入的问题(Bug #39632565)。
- 可传输表空间导入时索引页校验的问题(Bug #39295605)。
- InnoDB 全文检索 Boolean Mode 表达式的解析器嵌套问题(Bug #39234789)。
- 并发
COPY ALTER操作的问题(Bug #39134921)。 - 重命名被自引用 InnoDB 外键使用的列的问题(Bug #39091341)。
- 页重组产生尺寸不匹配时,InnoDB 可能继续使用重组后的页却不记录对应 redo;随后的插入加崩溃可导致恢复失败或页损坏。该不匹配现在按致命错误处理,不再让不一致的 redo 状态继续(Bug #116574、Bug #37258813)。
版本号接口变化
8.4.13 起 MySQL Server 支持四段式版本标识 MAJOR.MINOR.PATCH.UPDATE,可通过 VERSION_WITH_UPDATE()、只读全局变量 version_with_update 和 mysqld --version-with-update 获取。VERSION() 等既有接口保持三段式兼容语义。