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