编程 MySQL 8.4.13:漏掉的 OR 分支、gtid_executed 空洞与过期 AHI 条目

2026-11-01 21:02:25

MySQL 8.4.13:漏掉的 OR 分支、gtid_executed 空洞与过期 AHI 条目

MySQL 8.4.13 于 2026-10-20 发布,同批还有 9.7.4 LTS 与 26.10 Innovation。下面挑的是「不报错,但结果或状态是错的」这一类修复:语句正常执行、日志没有警告,但返回的行、gtid_executed 的内容或者页的内容已经不对了。

Optimizer:index_merge_union=off 时漏掉一个 disjunct

当 optimizer_switch 设成 index_merge_union=off 时,针对析取条件(OR)的 index-merge 执行计划可能漏掉一个 disjunct,返回不完整结果。发布说明举的例子是:一个预期返回四行的查询返回了

Empty set (0.00 sec)

贡献者 Kun Qin。(Bug #114885, Bug #36584358)

验证方式直接对着开关做对比:同一条带 OR 的查询,分别在 index_merge_union=on 与 off 下执行,比较返回行数与 EXPLAIN 里的计划形态。开关切换后行数变少、EXPLAIN 仍显示 index_merge 的,就是这条。

Replication:XA 的两处顺序问题

XA 这两条都不是「丢事务」,而是可见性顺序不对,靠轮询判断状态的应用会读到错误的中间态。

  • XA COMMIT 或 XA ROLLBACK 的 GTID 可能在 XA RECOVER 停止列出已完成 XID 之前就变可见。现在 XA RECOVER 会在 GTID 外部化之前先隐藏已 finalize 的事务。(Bug #36505659)
  • XA PREPARE 让 GTID 在 gtid_executed 中可见之后,XA RECOVER 仍可能漏掉这个 prepared XID。现在 XID 先可见、再更新 GTID 状态,所以「等到这个 GTID」就能保证另一个会话能看到这个 prepared 事务。(Bug #35872823)

验证要两个会话交叉做:一个会话 XA START / 写 / XA PREPARE,另一个会话同时轮询 gtid_executed 与 XA RECOVER,看两者出现顺序是否与上面的保证一致;commit 侧反过来,先 XA COMMIT,再看 XA RECOVER 里是否还残留该 XID。

Replication:binlogless 副本上 FLUSH PRIVILEGES 造成 gtid_executed 空洞

在使用 relay-log recovery、并行复制与 commit-order preservation 的 binlogless 副本上,被复制的 FLUSH PRIVILEGES 语句会在 mysql.gtid_executed 留下空洞。后果不是当场报错,而是重启后副本会重新请求已应用的事务,导致 duplicate 错误;或者在源端已清理所需 binlog 时直接失败。

验证:在该类副本上确认复制账号执行过 FLUSH PRIVILEGES 后,直接查 mysql.gtid_executed 是否出现不连续区间;然后重启副本观察是否出现重复事务报错。源端 PURGE BINARY LOGS 之后重启,是更容易暴露问题的时序。

MySQL Programs:mysqldump 对服务端返回标识符的校验

修复 mysqldump 对服务端返回的表标识符的处理,以及对服务端返回的结果、标识符、库名、表元数据的校验。(Bug #39905165, Bug #39832176, Bug #39832186, Bug #39832202)

这几条落在客户端侧:服务端返回的标识符不再被直接当作可信字符串使用,结果与元数据也各自过一遍校验。

InnoDB:三条与「过期页」相关

  • 导出的表被修改、表空间被 discard、再导入旧镜像之后,点查可能返回已 discard 表空间代际的行或值。现在过期的 Adaptive Hash Index 条目按 miss 处理,落回正常 B-tree 遍历。(Bug #39661793)
  • ALTER TABLE ... IMPORT TABLESPACE 失败后,被丢弃表空间的过期页可能在 evict 时触发断言:
[ERROR] [MY-013183] [InnoDB] Assertion failure: btr0sea.cc:1082:block->page.id.space() == index->space

(Bug #39661792)

  • InnoDB 页重组若产生尺寸不匹配,之前会继续使用重组后的页而不记录对应 redo;之后一次 insert 加崩溃会导致恢复失败或页损坏。现在这种不匹配按 fatal 处理。(Bug #116574, Bug #37258813)

第一条的复现路径就是描述里的三步:导出、改表、discard 表空间、导入旧镜像,然后对这张表做点查,看是否读到属于旧代际的值。第二条是在 IMPORT TABLESPACE 失败之后制造页淘汰,观察是否打到 btr0sea.cc:1082。第三条需要页重组后立刻 insert 再 crash,检验恢复能否走完。

SQL Syntax:EXCHANGE PARTITION 现在双向检查

ALTER TABLE ... EXCHANGE PARTITION 可能把违反目标表 CHECK 约束的行搬过去,或交换生成列使用了不同表达式的表。现在双向检查,包括 WITHOUT VALIDATION 跳过分区边界检查的情况;生成列定义不一致会被拒绝。报错形如

ERROR 3819 (HY000): Check constraint 't_swap_chk_1' is violated.

(Bug #120206, Bug #39634208)

验证:建一张带 CHECK 约束的分区表,往另一张同构表里插入违反该约束的行,执行 EXCHANGE PARTITION,应报 3819 而不是成功。生成列那一侧,把两张表的生成列表达式改成不同写法再交换,应被拒绝——注意这条在 WITHOUT VALIDATION 下同样生效。

Performance Schema:异步副本重启后的线程可见性

异步副本重启后,它的 I/O 与 SQL 线程可能出现在 INFORMATION_SCHEMA.PROCESSLIST,但在 performance_schema.processlist 缺失,直到复制被停掉再启动。现在保留带 process list ID 的前台系统线程,空用户名显示为 system user。

四段式版本号

MySQL Server 现在支持四段式版本号 MAJOR.MINOR.PATCH.UPDATE,update 分量为空时表示为 0。可通过 VERSION_WITH_UPDATE()、只读全局变量 version_with_update、mysqld --version-with-update 获取。已有一致的三段式语义保留。

打包变化

libevent 2.1.11 → 2.1.13;捆绑的 OpenTelemetry 依赖 opentelemetry-cpp 1.23.0 → 1.27.0、opentelemetry-proto 1.7.0 → 1.10.0;TIRPC 升到 1.3.7。EL10 aarch64 上商业包与 mariadb11.8 的文件冲突问题,通过声明额外的 obsoletes 解决。

复制全文 生成海报 MySQL 数据库 复制 优化器

推荐文章

程序员茄子在线接单