GANKUDADIZ
BACK_TO_BLOG
TECH_LOG :: 2026.04.17

关于 MySQL 整数显示宽度的排查:为什么同一张表会出现 `bigint(20) unsigned` 和 `bigint unsigned`

Avatar
By Gankudadiz · 2 min read · 11 views

今天排查了一个看起来很小,但很容易把人带偏的问题。

同一张业务表、同一个主键字段 id,在两套本地开发环境里看到的 SHOW CREATE TABLE 结果不一样:

  • Windows 11 本地 MySQL 8.0.12 显示为 bigint(20) unsigned

  • WSL2 Ubuntu 24.04 本地 MySQL 8.0.45-0ubuntu0.24.04.1 显示为 bigint unsigned

如果只是扫一眼,很容易以为两边 schema 不一致,甚至怀疑某次迁移没有跑齐,或者某个环境被手工改过字段类型。更麻烦的是,这个字段还是主键自增列。只要涉及 unsignedauto_increment、外键关联,经验上就不能靠猜。

所以这次没有继续停留在图形界面,而是直接回到数据库本身,把问题拆成三层来看:

  1. SHOW CREATE TABLE 到底返回了什么
  2. information_schema.columns 里的元数据到底是什么
  3. 这两个版本之间,MySQL 官方是否改过整数类型的输出规则

我最后确认到的结论是:这不是 schema 差异,而是 MySQL 小版本差异导致的 DDL 输出差异。

先说现象

两边的真实建表语句,除了主键字段的显示方式不同,其余部分一致。抽象成一个最小示例如下。

Windows 11,MySQL 8.0.12

CREATE TABLE `example_table` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

WSL2,MySQL 8.0.45-0ubuntu0.24.04.1

CREATE TABLE `example_table` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

如果只盯着这一行,直觉上会觉得少了一个 (20),像是字段定义发生了变化。但继续往下查 information_schema.columns 后就会发现,真正关键的属性并没有变:

  • DATA_TYPE 仍然是 bigint
  • COLUMN_TYPE 仍然保留 unsigned
  • EXTRA 仍然是 auto_increment
  • 主键属性没有变化

也就是说,影响行为的部分没有变,变的是 MySQL 输出 DDL 的方式。

关键变化发生在 MySQL 8.0.19

这件事的分界点不是 “Windows 和 WSL2”,也不是 “Navicat 和命令行”,而是 MySQL 8.0.19

MySQL 官方文档给出的时间线很清楚:

  • 在 MySQL 8.0.17,整数类型的 display width 已被标记为 deprecated
  • 到 MySQL 8.0.19SHOW CREATEDESCRIBE 以及相关 INFORMATION_SCHEMA 输出中,默认不再显示整数 display width
  • 例外主要是 TINYINT(1) 和带 ZEROFILL 的整数类型

这正好解释了这次现象:

  • 8.0.12 仍然沿用旧行为,所以 SHOW CREATE TABLE 输出 bigint(20) unsigned
  • 8.0.45 已经包含 8.0.19 之后的行为,所以 SHOW CREATE TABLE 输出 bigint unsigned

这不是字段被改窄了,也不是 unsigned 丢了,更不是自增列定义发生了变化。只是 MySQL 从 8.0.19 开始,默认不再把整数 display width 打印出来。

为什么还能查到“20”

排查时还有一个容易让人困惑的点:明明新版 SHOW CREATE TABLE 里已经不显示 (20) 了,为什么从 information_schema.columns 里还能查到 NUMERIC_PRECISION = 20

原因是,这两个 “20” 不是一个概念。

旧版 SHOW CREATE TABLE 里看到的 bigint(20),说的是整数 display width,这个语法历史很长,但它本来就不决定存储大小,也不改变值域。

information_schema.columns.NUMERIC_PRECISION = 20,反映的是这个数值类型的精度元数据。对于 BIGINT UNSIGNED,最大值是:

18446744073709551615

这是一个 20 位十进制数字,所以 precision 是 20。这部分元数据保留,并不意味着新版 MySQL 还在用 display width 参与字段定义展示。

所以这次排查里最值得记住的一点是:

  • SHOW CREATE TABLE 看到的是当前版本如何“打印”DDL
  • NUMERIC_PRECISION 看到的是数值类型本身的精度元数据

两者都可能出现 20,但语义并不相同。

这类差异会不会影响业务

如果两边真实语义一致,只是一个显示为 bigint(20) unsigned,另一个显示为 bigint unsigned,那对业务没有实际影响。

不会受影响的部分包括:

  • 主键自增行为
  • 存储字节数
  • 索引行为
  • 外键兼容性
  • Laravel 这类框架里 id()foreignId() 的匹配关系

真正应该关注的不是 (20) 在不在,而是下面这些属性是否一致:

  • 是否都是 bigint
  • 是否都带 unsigned
  • 是否都为 NOT NULL
  • 是否都启用了 AUTO_INCREMENT
  • 主键和外键定义是否一致

如果这些都一致,那么这类差异就是“显示差异”,不是“结构差异”。

这次排查留下的一个习惯

以后再遇到跨环境对比表结构的问题,我会尽量先跳过 GUI 的直觉判断,优先做三件事:

SHOW CREATE TABLE example_table;

SELECT VERSION();

SELECT
    COLUMN_NAME,
    COLUMN_TYPE,
    DATA_TYPE,
    NUMERIC_PRECISION,
    COLUMN_KEY,
    EXTRA
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_database'
  AND TABLE_NAME = 'example_table'
  AND COLUMN_NAME = 'id';

先确认版本,再确认 DDL,再确认元数据。这样能比较快地把“显示差异”和“真实 schema 差异”分开,不会被一个 (20) 带着跑偏。

今天这个问题不算大,但很适合记下来。因为它提醒我一件很基础、但也很容易在忙的时候忘掉的事:数据库问题里,字符串长得像不像,经常没有语义是否一致重要。

参考资料

评论 (0)

还没有评论,来留下第一条想法吧。

[DRAFT_RESTORED]

已恢复你上次未提交的评论草稿。

草稿仅保留在当前标签页;提交成功后会清空,关闭标签页后会自动失效。

ACTION: