编程 MySQL 9.7 把 UNIQUE NOT NULL 当主键等价:开着 sql_generate_invisible_primary_key 也不再补 GIPK

2026-11-04 21:05:49

MySQL 9.7 把 UNIQUE NOT NULL 视为主键等价:GIPK 不再被自动添加,行大小校验在 9.7.4 回退

信源:MySQL 9.7.0 release notes(2026-04-21)与 9.7.4 release notes(2026-10-20),dev.mysql.com。

9.7 这条线对 schema 维护者有两个直接影响:UNIQUE NOT NULL 的语义被抬高,以及 InnoDB 行大小估算在 9.7.0 收紧、9.7.4 又放开大半。下面按 Atomic DDL Notes 与 InnoDB 两部分的顺序记录。

UNIQUE NOT NULL 在 CREATE/ALTER 里成为「主键等价」

先看被修掉的行为。当 sql_generate_invisible_primary_key=ON 时,即使表上已经有 UNIQUE NOT NULL 键,MySQL 仍会补一个 Generated Invisible Primary Key(GIPK);如果那个唯一键本身是自增的,GIPK 创建会直接失败(Bug #38195294)。

从 9.7 起,UNIQUE NOT NULL 键在 CREATE 和 ALTER 语句中被视为 Primary Key-equivalent。带这类键的表不再被补 GIPK。

复现方式——普通唯一键,不应该再出现 _my_row_id:

SET SESSION sql_generate_invisible_primary_key = ON;

CREATE TABLE t_uniq (
id BIGINT UNSIGNED NOT NULL,
UNIQUE KEY uk_id (id)
) ENGINE=InnoDB;

自增唯一键,此前 GIPK 创建会直接失败:

CREATE TABLE t_uniq_ai (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
UNIQUE KEY uk_id (id)
) ENGINE=InnoDB;

自查生成的列:

SELECT column_name, extra
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 't_uniq';

SHOW CREATE TABLE t_uniq\G 里也应当看不到隐式主键列。

SQL_REQUIRE_PRIMARY_KEY 下终于能建这类表

相关修复的另一半:以前在 SQL_REQUIRE_PRIMARY_KEY 启用时,无法创建带 Primary Key Equivalent 的表;现在可以。也就是说「有 UNIQUE NOT NULL 但没显式主键」的定义,在强制主键的实例上不再被拒。

SET SESSION sql_require_primary_key = ON;

CREATE TABLE t_req_pk (
id BIGINT UNSIGNED NOT NULL,
UNIQUE KEY uk_id (id)
) ENGINE=InnoDB;

顺带修掉的两处

  • 数据库目录在文件系统上缺失时,DROP DATABASE 现在仍能执行,保持数据字典一致(Bug #38711166)。
  • mysqlbinlog 输出里 CREATE TABLE ... START TRANSACTION 之后紧跟以 # 开头的注释时,回放会报 ERROR 3977(Only BINLOG INSERT, COMMIT and ROLLBACK statements are allowed after CREATE TABLE with START TRANSACTION statement),已修(Bug #36313112)。

InnoDB 行大小校验:9.7.0 收紧,9.7.4 放回旧行为

9.7.0 中 InnoDB 更准确地计算索引记录最大尺寸(含索引前缀字段和空间 GEOMETRY 索引),目的是更早拿到 page latch,避免与并发 B-tree 搜索死锁。副作用是部分超过文档上限的 CREATE TABLE / ALTER TABLE 现在会被正确拒绝(Bug #39129182)。

9.7.4(2026-10-20)把大部分 DDL 的旧校验行为放了回来——因为上面那版改动让一些更早 LTS 能接受的表定义被意外拒绝。准确估算只在需要防止 B-tree 死锁的地方保留;ALTER TABLE ... ALGORITHM=INSTANT 继续用更严格的算法,以安全处理大默认值(Bug #120323 / Bug #39249507,Percona 与 Amazon 团队贡献)。

排查时先确认实例落在哪个小版本:

SELECT VERSION();

在 9.7.0–9.7.3 上遇到建表或改表被拒、而定义在旧 LTS 上能过,先核对是不是行大小估算导致的;带 ALGORITHM=INSTANT 的 ALTER 即使在 9.7.4 上仍走严格算法。

数据字典:CONVERT TO CHARACTER SET 不再动时间类型的字符集

9.7.0 起,ALTER TABLE ... CONVERT TO CHARACTER SET 不再改变时间类型在数据字典与 SDI 里的字符集,服务器对这些类型固定使用 my_charset_latin1。此前 SDI 里的 collation_id 与 Field 对象不一致。

需要核对 SDI 内容时可以用 ibd2sdi 看表空间里的定义:

ibd2sdi /var/lib/mysql//.ibd | grep -i collation
复制全文 生成海报 MySQL 数据库 DDL GIPK

推荐文章

程序员茄子在线接单