MySQL 的ID用什么数据类型更好?主键ID选型实战

前言

建表第一件事就是设计主键ID,很多新手直接无脑写 id INT PRIMARY KEY AUTO_INCREMENT,等到业务数据量上来,INT溢出、分库分表主键冲突、性能瓶颈等问题集中爆发。

主键ID选型不只是选字段类型,还要考虑:数据上限、存储空间、写入性能、分库分表、索引体积、业务是否允许自增。本文对比常用ID类型,讲清楚适用场景和线上踩坑点。

一、先看各数字类型存储范围与占用空间

只列出主键最常用的整数类型(无符号 unsigned)

| 类型 | 占用字节 | 无符号取值范围 | 最大行数参考 |

| ---- | ---- | ---- | ---- |

| TINYINT | 1 | 0 ~ 255 | 255条,几乎不用做主键 |

| SMALLINT | 2 | 0 ~ 65535 | 6万+,小型配置表可用 |

| MEDIUMINT | 3 | 0 ~ 16777215 | 约1600万 |

| INT | 4 | 0 ~ 4294967295 | 约42亿 |

| BIGINT | 8 | 0 ~ 18446744073709551615 | 接近1.8e19,几乎不会溢出 |
重点提醒:不加UNSIGNED,最大值直接减半 。

INT 有符号范围:-2147483648 ~ 2147483647,上限仅21亿。很多项目踩坑,业务增长到2000万~2亿时,ID就逼近上限。

二、方案1:自增INT(INT UNSIGNED AUTO_INCREMENT)

sql 复制代码
CREATE TABLE demo (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

✅ 优点

  1. 占用4字节,相比BIGINT节省一半存储空间;
  2. 顺序自增,主键索引有序,InnoDB聚簇索引写入性能好;
  3. 简单,数据库自动维护,开发成本低;
  4. 索引体积更小,内存可以缓存更多索引页。

❌ 缺点

  1. 上限42亿,业务持续增长有溢出风险;
  2. 分库分表场景,多库自增ID会重复;
  3. ID连续,容易被爬虫遍历(比如接口?id=1001遍历所有数据);
  4. 数据删除后ID不会回收,只是空号,不能复用。

✅ 适合场景:

中小型业务,预估总数据远小于亿级、不分库分表、单库单表。

例如后台配置表、内部管理系统。

❌ 不适合:

高增长业务、用户表、订单表,未来可能分库分表的业务。

三、方案2:自增BIGINT(BIGINT UNSIGNED AUTO_INCREMENT)

sql 复制代码
CREATE TABLE demo (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

✅ 优点

  1. 上限极大,几乎不用担心溢出;
  2. 顺序写入,InnoDB聚簇索引友好;
  3. 单库单表开发简单。

❌ 缺点

  1. 8字节,比INT多一倍存储;表越大,主键、二级索引体积都会变大(二级索引会保存主键值);
  2. 同样存在分库分表ID冲突、ID可被遍历的问题。

✅ 适合场景:

绝大多数互联网业务,用户、订单、流水表;单库未来有可能分库分表,但是暂时不分。

【推荐默认选型】新项目如果拿不准,直接选 BIGINT UNSIGNED。存储空间成本在现代服务器上基本可以接受,避免后期INT溢出改表的灾难。
⚠️ 线上重大事故场景:业务前期用INT,数据增长到20亿,ID即将溢出,大表修改字段类型是重量级DDL,锁表、耗时极长,风险极高。

四、方案3:字符串类型ID(UUID / UUIDv4 /雪花ID存CHAR/VARCHAR)

sql 复制代码
-- 示例,字符串主键
id CHAR(36) PRIMARY KEY

✅ 优点

  1. 全局唯一,多库分库分表不会冲突;
  2. ID无规律,不能简单通过递增遍历数据。

❌ 缺点

  1. 占用空间大(36字节),索引膨胀严重;
  2. UUIDv4是完全随机,无序写入。InnoDB聚簇索引要求主键有序,随机ID会造成大量页分裂、碎片,写入性能大幅下降;
  3. 更长的主键,所有二级索引都会变大,缓冲池利用率变差;
  4. 字符串比较比整数慢,查询、关联JOIN性能弱于数字主键。

优化方案:UUIDv1 / UUIDv7(有序UUID),可以缓解页分裂问题。

✅ 适合场景:

需要跨数据库全局唯一,对写入性能要求不极致的场景。

❌ 不适合:高并发写入的核心业务表(订单、流水)。

五、方案4:雪花算法ID,存储在BIGINT

雪花ID本质是一个64bit数字,可以直接放入BIGINT。

✅ 优点

  1. 64位数字,全局唯一,支持分布式、分库分表;
  2. 包含时间戳,ID自带时间信息,可以粗略排序;
  3. 存储占用8字节,和自增BIGINT一样,索引体积小;
  4. 不会暴露业务数据总量。

❌ 缺点

  1. 依赖外部服务或者程序生成ID,数据库不能自动生成;
  2. 需要保证机器ID不重复;时钟回拨会产生重复ID,需要业务层处理;
  3. 不是严格连续自增。

✅ 适合场景:

分布式系统、分库分表、高并发核心业务(订单、用户)。

六、不推荐的ID方案

  1. VARCHAR自增ID:完全没必要,字符串索引性能差;
  2. 复合主键:尽量避免做主表主键,二级索引开销巨大;
  3. 使用业务字段做主键(手机号、身份证号):业务字段会变更,主键一旦修改,所有二级索引都要同步更新,灾难级开销。

原则:主键尽量无业务含义,只作为唯一标识。

七、InnoDB主键核心底层原理(为什么顺序很重要)

InnoDB是聚簇索引,整张表的数据存放在主键B+树叶子节点。

  • 有序主键(自增ID):新数据持续追加到B+树末尾,很少发生页分裂,写入快,碎片少。
  • 无序主键(随机UUID):新ID随机插入B+树中间,频繁页分裂,IO暴涨,性能下降。

这也是为什么不推荐直接使用UUIDv4作为主键最核心的原因。

八、选型决策速查表

场景 推荐类型
小型内部系统,数据预估千万以内,单库 INT UNSIGNED AUTO_INCREMENT
通用新项目,互联网业务,单库,不确定未来数据量 BIGINT UNSIGNED AUTO_INCREMENT
分布式/分库分表,高并发 BIGINT 存储雪花ID
跨系统数据同步,不需要超高写入性能 CHAR(36) 有序UUID(UUIDv7)

九、线上踩坑清单

  1. INT不加UNSIGNED,上限直接减半,更容易溢出;
  2. 前期图省钱选INT,后期数据量暴涨,大表ALTER修改字段类型,锁表引发故障;
  3. 直接用UUIDv4做主键,高并发下数据库写入卡顿;
  4. 雪花ID存入VARCHAR,白白浪费空间,丧失数字类型性能优势;
  5. 主键设置有业务含义,业务变更时要修改主键;
  6. 主键太长,二级索引体积膨胀,导致内存缓存命中率下降,查询变慢。

十、最佳实践总结

  1. 主键尽量选用无业务含义、固定长度、有序的字段;
  2. 单库新项目,拿不准就直接 BIGINT UNSIGNED AUTO_INCREMENT,牺牲少量磁盘空间换取安全,规避溢出风险;
  3. 分布式、分库分表场景,使用雪花ID,存入BIGINT;
  4. 尽量避免直接使用原生UUIDv4作为主键;
  5. 不要用手机号、邮箱等业务唯一字段代替主键,可以单独建唯一索引。

一句话总结:小表可以INT;绝大多数业务优先BIGINT自增;分布式场景BIGINT存雪花ID;谨慎使用字符串UUID主键。

相关推荐
蓝胖的四次元口袋2 小时前
Redis 知识梳理(3)
数据库·redis·缓存
一铭111992 小时前
海豹办公 1.9.15 跨端办公软件实践 文档兼容 Access 数据库与移动 WebView 排错
android·数据库·electron·开源软件·access
Lynne3092 小时前
TSN 在变电站自动化通信网络升级中的应用价值与技术挑战
服务器·网络·数据库
弈栈录2 小时前
用户、会话与消息表应该如何设计
数据库·后端
内存漫游2 小时前
Checkpoint 与 Crash Recovery:数据库恢复机制入门
数据库
2301_786756603 小时前
中科院分区表停发后,期刊评价的技术逻辑:从期刊超越指数到多指标选刊方案
java·数据库·windows
闲云野鹤在人间3 小时前
Ubuntu 24.04 安装过程
服务器·数据库·ubuntu
斯内普吖3 小时前
(开源)线上花店系统实战指南 基于 Java + SSM + Vue + MySQL
java·vue.js·mysql·开源