BIGINT主键使索引变大,因B+树每层非叶节点和聚簇索引叶子节点均存储主键值,联合索引首列用BIGINT还会导致二级索引冗余存储;亿级表可能多占数十GB,加剧内存压力与查询延迟。

为什么BIGINT主键会让索引变大
BIGINT 占 8 字节,INT 占 4 字节——单行多占 4 字节,看起来不多,但放到 B+ 树索引里会被放大:每层非叶子节点都要存主键值做路由,叶子节点还要存整行数据(聚簇索引)。对亿级表,索引体积可能多出几十 GB,内存缓存压力陡增,查询延迟明显上升。
更隐蔽的问题是:如果用 BIGINT 作联合索引的首列(比如 (id, created_at)),那所有二级索引的叶子节点都会冗余存储这个 8 字节 id,进一步推高索引总大小。
别盲目上BIGINT:先确认你真需要它
很多团队一听说“主键快用完了”就直接切 BIGINT,结果发现年增千万数据,INT UNSIGNED 还能撑 40 年——纯属过度设计。
- 查真实增长速率:
SELECT COUNT(*) FROM your_table WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 YEAR); - 算剩余空间:
SELECT (4294967295 - MAX(id)) AS remaining FROM your_table;(针对INT UNSIGNED) - 注意:如果表长期有删改,
Auto_increment值可能远大于MAX(id),用SHOW TABLE STATUS LIKE 'your_table'看Auto_increment字段才准
替代方案比硬切BIGINT更轻量
不是所有场景都必须用 BIGINT。以下几种方式开销小、风险低,适合中等规模业务:
- 改自增起始值:
ALTER TABLE your_table AUTO_INCREMENT = 2147483647 - 1000000;(留缓冲,避免瞬间溢出) - 调大自增步长:
SET SESSION auto_increment_increment = 10;或全局改配置(需评估分库分表兼容性) - 用时间戳 + 序号组合生成 ID(如
20260508123456789),存在BIGINT字段里但不依赖AUTO_INCREMENT,可控且无锁表风险 - 如果已用
UUID或雪花 ID,干脆把主键改成CHAR(32)或BINARY(16),关掉AUTO_INCREMENT——但要注意字符串主键对 JOIN 和排序的性能影响
真要切BIGINT?这些点漏一个就翻车
线上执行 ALTER TABLE ... MODIFY COLUMN id BIGINT 不是改个类型那么简单:
- 外键必须同步改:所有引用该
id的子表字段也得MODIFY COLUMN xxx_id BIGINT,否则 DDL 直接报错 - 应用层变量类型要对齐:Java 里
int接收BIGINT会截断,Go 里int默认是 32 位,得换成int64 - ORM 映射别漏:MyBatis 的
resultType、Hibernate 的@Column(columnDefinition = "BIGINT")都得更新 - JSON 序列化注意:JavaScript 的
Number只能精确表示到2^53-1,超限 ID 会丢失精度,得转成字符串传
最常被忽略的是:改完后没重置 Auto_increment 值,导致新插入从 1 开始,和已有数据冲突。执行完 MODIFY 后务必跟一句:ALTER TABLE your_table AUTO_INCREMENT = (SELECT MAX(id) FROM your_table) + 1;


















