在实际开发过程中Mysql该选用ID什么ID作为主键?
在实际开发过程中Mysql该如何设置ID
核心原则
首先,记住一个核心原则:主键应该是简短、稳定、唯一且递增的。
- 简短: 占用存储空间小,索引更高效。
- 稳定: 一旦生成,最好不要改变。更改主键会引发连锁的更新操作,非常昂贵。
- 唯一: 这是主键的根本。
- 递增: 对于使用
B+Tree索引的 InnoDB 来说,递增的主键可以保证顺序写入,极大减少页分裂,提高插入性能。
方案一:自增整数(AUTO_INCREMENT) - 最常用
这是 MySQL 默认推荐的方式,也是绝大多数场景下的最佳选择。
DDL 示例:
CREATE TABLE `users` ( |
优点:
- 性能极佳: 插入新数据时无需为主键列指定值,数据库自动在当前最大值上加 1。顺序写入,效率最高,能充分利用索引的局部性原理。
- 存储空间小:
INT(4字节) 或BIGINT(8字节) 类型非常紧凑。 - 简单可靠: 数据库自身保证唯一性和递增性,业务代码无需关心 ID 生成逻辑。
缺点:
- 不便分库分表: 在分布式数据库架构下,多个数据库实例可能会生成相同的 ID,导致冲突。需要额外的分布式 ID 生成方案(如设置不同实例的自增步长和偏移量,但这很复杂且不推荐)。
- 安全性问题: ID 是连续的,容易被爬虫爬取全部数据(例如,将 URL 中的
id=1改为id=2即可访问下一条)。但这通常不是大问题,权限校验才是关键。 - 业务含义泄露: 有时我们不希望用户通过 ID 推测出公司的业务量(例如,用户 ID 为 10086,可能代表公司有 1 万多个用户)。
适用场景:
- 几乎所有单机 MySQL 数据库。
- 非分布式的、简单的 Web 应用、后台管理系统、企业内部系统等。
方案二:UUID(Universally Unique Identifier) - 用于分布式系统
UUID 是一个 128 位(16字节)的数字,通常以 36 个字符的字符串形式表示(如 550e8400-e29b-41d4-a716-446655440000)。
DDL 示例:
CREATE TABLE `orders` ( |
优点:
- 全局唯一: 这是它最大的优势。在分布式系统中,不同节点无需协调即可独立生成绝对不会冲突的 ID,非常适合分库分表场景。
- 安全性更高: ID 毫无规律,无法被猜测,一定程度上避免了数据被遍历的风险。
缺点:
- 性能灾难:
- 存储空间大: 字符串形式占用 36 字节,即使是优化为
BINARY(16)也要 16 字节,是BIGINT的两倍。导致主键索引和二级索引都非常臃肿。 - 插入性能差: UUID 是随机的,新插入的行无法简单地追加到索引末尾,而是需要插入到中间的某个位置,会导致大量的页分裂和索引碎片,严重拖慢写入速度。
- 缓存不友好: 随机 IO 无法利用磁盘和内存的缓存,查询性能也会受到影响。
- 存储空间大: 字符串形式占用 36 字节,即使是优化为
- 可读性差: 对人类不友好,调试和排查问题时很不方便。
适用场景:
- 需要提前生成 ID: 在事务提交前,业务就需要拿到 ID 用于后续操作。
- 真正的分布式数据库系统(如分库分表的超大集群),且没有其他更好的分布式 ID 生成方案时。
- 数据需要离线合并,且无法保证唯一性的场景。
强烈建议:除非你非常确定你的业务是分布式且需要提前知道 ID,否则不要使用 UUID 作为主键。
方案三:雪花算法(Snowflake)及其变体 - 分布式系统的最佳实践
这是对“自增ID”和“UUID”的一种完美折中方案。它生成一个 64位(8字节) 的 BIGINT 长整数,结构如下:
| 1bit (符号位,恒为0) | 41bit (时间戳,毫秒级) | 10bit (工作机器ID) | 12bit (序列号) |
|---|---|---|---|
| 0 | 00001010101010101010… | 0000000001 | 000000000000 |
工作原理: 在同一毫秒内,同一台机器上的生成器通过递增序列号来生成多个 ID。如果毫秒数递增,序列号就重置为 0。
DDL 示例:
生成 ID 的工作通常在应用层完成(如 Java 程序使用 Hutool 等工具包),数据库只负责存储。
CREATE TABLE `distributed_table` ( |
优点:
- 分布式友好: 通过分配不同的“机器ID”,不同节点生成的 ID 绝对不会冲突。
- 性能优异: 生成的 ID 是趋势递增的(整体上看是随时间变大的),对 InnoDB 的聚集索引非常友好,插入性能接近自增 ID。
- 存储空间小: 仅占 8 字节,和自增
BIGINT一样。 - 高可用: 不依赖数据库自身,即使数据库短暂不可用,应用也能继续生成 ID。
缺点:
- 实现复杂: 需要在应用层引入额外的代码或服务(如独立的 ID 生成器)来保证“机器ID”的唯一性和时钟的正确性(防止时钟回拨)。
- 依赖系统时钟: 如果发生时钟回拨(服务器时间被调慢),可能会导致生成重复 ID。
适用场景:
- 高并发的分布式系统、微服务架构。
- 需要进行分库分表 的大型互联网应用。
- 几乎所有需要全局唯一 ID 且对性能有要求的场景。
总结与决策图
为了帮助你快速做出选择,可以参考以下决策流程:
flowchart TD |
最终建议:
- 默认选择: 对于 99% 的应用,直接使用
BIGINT UNSIGNED AUTO_INCREMENT。BIGINT的范围足够大到让你忘记溢出的问题(从 0 到 184亿亿)。 - 分布式架构: 如果你在做微服务或明确知道要分库分表,首选雪花算法或其变体(如美团Leaf、百度UidGenerator)。这是在性能、存储成本和分布式需求之间最好的权衡。
- 尽量避免使用 UUID: 除非你非常清楚它的代价并且能接受,或者你的场景极其特殊(如极高的离散性要求)。
额外提示:
- 永远不要用具有业务意义的字段(如手机号、身份证号、邮箱)做主键,因为业务含义可能会变化。
- 主键就只负责唯一标识一行数据,它唯一的使命就是“短、稳、快”。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Lazy的个人博客!




