• 为什么MySQL不推荐使用雪花id和uuid做主键?大部分人都会答错!

为什么MySQL不推荐使用雪花id和uuid做主键?大部分人都会答错!

2025-08-16 12:33:37 栏目:宝塔面板 0 阅读

兄弟们,今天咱们来聊个数据库圈的 “未解之谜”—— 为啥 MySQL 打死不待见雪花 ID 和 UUID 当主键?这个问题要是搁技术群里一问,十个人能答出八种花样,但真正能说到点子上的,可能连三个都没有。别急,今天咱们就来一场 “主键侦探之旅”,把这个问题彻底盘明白。

一、主键:数据库的 “户口本”

咱们先得搞清楚主键是干啥的。简单来说,主键就是数据库里每一行数据的 “身份证号”,得满足三个条件:唯一性、非空性、稳定性。就像你去派出所办户口,每个人的身份证号必须独一无二,还不能随便改,不然整个户籍系统就乱套了。

在 MySQL 里,主键可不只是个标识这么简单,它还直接影响着数据的存储和查询效率。尤其是 InnoDB 引擎,它采用的是聚簇索引(Clustered Index),数据行直接存储在主键索引的叶子节点上。这就好比图书馆的书架,每本书的位置是按照主键的顺序排列的。如果主键是自增的整数,就像按照书名拼音排序,找起书来又快又方便;但要是用 UUID 这种随机字符串,就好比把书随机乱扔,每次找书都得把整个书架翻个底朝天,效率能高才怪。

二、UUID:看似完美的 “长脖子怪物”

UUID 全称是通用唯一识别码,长这样:550e8400-e29b-41d4-a716-446655440000。听起来挺唬人,其实就是个由数字和字母组成的 36 位字符串。它的优点很明显:全局唯一,就算你在地球这边生成,火星那边也绝对不会重复。所以很多人觉得,用 UUID 做主键简直完美,尤其是在分布式系统里。

但 MySQL 对 UUID 那是一万个嫌弃,为啥呢?咱们来扒扒它的 “黑历史”。

1. 存储空间的 “黑洞”

UUID 是字符串类型,存储它需要占用 36 个字节的空间。而自增 ID 如果用 BIGINT 类型,只需要 8 个字节。打个比方,自增 ID 就像一辆自行车,小巧灵活;UUID 则是一辆大卡车,占地方不说,油耗还高。如果你的表有 1000 万条数据,用 UUID 做主键,光是主键字段就要多占 280MB 的空间,这还没算索引的开销呢。

2. 索引效率的 “灾难现场”

InnoDB 的聚簇索引是按照主键顺序存储的,而 UUID 是随机生成的,这就导致数据插入时会随机写入到索引的不同位置。想象一下,你每次往书架上放书都得随机找个位置塞进去,时间长了,书架上到处都是空隙,这就是所谓的页分裂(Page Split)。页分裂会导致索引碎片化,查询时需要扫描更多的磁盘块,性能直线下降。

有人做过测试,插入 100 万条数据,自增 ID 只需要 180 秒,而 UUID 足足用了 720 秒,差距整整 4 倍!更要命的是,随着数据量的增加,UUID 的性能会呈指数级下降。当数据量达到 350 万条时,插入速度可能会比自增 ID 慢 10 倍以上。

3. 查询性能的 “慢性毒药”

UUID 作为主键,在查询时也会带来额外的开销。比如你想查询某个用户的信息,SQL 语句是 WHERE id = '550e8400-e29b-41d4-a716-446655440000'。这时候,MySQL 需要对字符串进行逐字符比较,而字符串比较比整数比较慢得多。再加上索引碎片化的问题,查询性能更是雪上加霜。

三、雪花 ID:看似有序的 “时间炸弹”

雪花 ID(Snowflake)是 Twitter 开源的分布式 ID 生成算法,它生成的 ID 是 64 位的长整型,结构如下:

  • 1 位符号位(固定为 0)
  • 41 位时间戳(精确到毫秒)
  • 10 位工作机器 ID(数据中心 ID + 机器 ID)
  • 12 位序列号(同一毫秒内的并发计数)

雪花 ID 的优点很明显:全局唯一、有序递增,而且生成效率极高,单机每秒可以生成数百万个 ID。听起来是不是比 UUID 好多了?但 MySQL 对它同样不感冒,为啥呢?

1. 长度的 “甜蜜负担”

虽然雪花 ID 是数字类型,但它占用 8 个字节的存储空间,比自增 ID 的 4 字节(INT 类型)还是多了一倍。如果你的表数据量特别大,比如每天新增几千万条记录,这多出来的存储空间也是一笔不小的开支。

2. 排序的 “陷阱”

雪花 ID 的时间戳部分虽然保证了整体有序,但在同一毫秒内,ID 是按照序列号递增的。这就导致在高并发场景下,大量数据会集中插入到索引的末尾,形成热点写(Hot Spot Writes)。就像春运时的火车站,所有人都挤在检票口,很容易造成拥堵。这种情况下,InnoDB 的间隙锁(Gap Lock)竞争会加剧,导致性能下降甚至死锁。

3. 时钟回拨的 “定时炸弹”

雪花 ID 的生成依赖服务器的时间戳,如果服务器的时钟因为 NTP 同步、硬件故障等原因出现回拨(时间倒退),就会生成重复的 ID。这就好比你穿越回过去,结果发现自己和过去的自己同时存在,那场面简直混乱不堪。虽然可以通过一些策略来避免,比如记录上一次生成 ID 的时间戳,发现回拨时拒绝服务或等待,但这无疑增加了系统的复杂性和维护成本。

四、自增 ID:MySQL 的 “亲儿子”

说了这么多 UUID 和雪花 ID 的坏话,咱们来聊聊 MySQL 的 “心头好”—— 自增 ID。自增 ID 是 MySQL 内置的主键生成方式,通过 AUTO_INCREMENT 属性实现。它的优点简直不要太多:

1. 存储空间的 “经济适用男”

自增 ID 通常使用 INT 或 BIGINT 类型,分别占用 4 字节和 8 字节的空间,比 UUID 和雪花 ID 节省了大量的存储空间。这就好比你买房子,自增 ID 是小户型,价格便宜还实用;UUID 和雪花 ID 是大别墅,虽然豪华但性价比太低。

2. 插入性能的 “闪电侠”

自增 ID 是顺序递增的,数据插入时会追加到索引的末尾,不会产生页分裂。就像排队上车,每个人都按顺序往后排,队伍整整齐齐,效率自然高。有人测试过,插入 100 万条数据,自增 ID 只需要 180 秒,而雪花 ID 需要 224 秒,UUID 更是需要 720 秒。这差距,就像自行车、汽车和飞机的区别。

3. 查询性能的 “王者”

自增 ID 的索引结构紧凑,查询时只需要扫描少量的磁盘块,性能自然高。比如你想查询 id=12345 的记录,MySQL 可以直接定位到对应的索引位置,就像在字典里查字,直接翻到对应的页码就行。而 UUID 和雪花 ID 由于索引碎片化,查询时需要扫描更多的磁盘块,就像在一堆乱码里找特定的字符串,效率可想而知。

五、分布式系统的 “救星”

虽然自增 ID 在单机环境下表现出色,但在分布式系统中,它的缺点也很明显:无法保证全局唯一性。比如你有两个数据库实例,每个实例都用自增 ID,就很容易出现 ID 冲突。那怎么办呢?别急,咱们有以下几种解决方案:

1. 号段模式(Segment Mode)

号段模式是美团 Leaf 框架采用的一种分布式 ID 生成策略。它的原理是从数据库批量获取 ID 号段,缓存在内存中,减少对数据库的频繁访问。比如数据库分配一个号段 1-1000 给应用 A,1001-2000 给应用 B,每个应用在本地生成 ID,用完后再向数据库申请新的号段。这种方式既保证了全局唯一性,又避免了雪花 ID 的时钟回拨问题,性能也不错。

2. 雪花算法的改进版

Twitter 的雪花算法虽然有缺点,但经过改进后,仍然是分布式 ID 生成的主流方案之一。比如可以增加时钟回拨的容错机制,或者调整工作机器 ID 和序列号的位数,以适应不同的业务场景。美团、百度等大厂都有自己的雪花算法改进版,比如美团的 Leaf-snowflake,百度的 UidGenerator 等。

3. 有序 UUID

MySQL 8.0 引入了有序 UUID(Order UUID),通过交换时间高位与低位,使 UUID 按时间有序递增。这样既保证了全局唯一性,又减少了页分裂的问题。有序 UUID 以二进制形式存储,占用 16 字节的空间,插入性能接近自增 ID。比如插入 100 万条数据,有序 UUID 只需要 224 秒,而原生 UUID 需要 720 秒。

六、常见误区大揭秘

误区一:UUID 和雪花 ID 能避免信息泄露

很多人觉得,UUID 和雪花 ID 是随机生成的,不会暴露业务数据量。比如用户注册时,ID 是随机的,别人就猜不到注册了多少用户。但实际上,雪花 ID 的时间戳部分可以反推出数据生成的时间,而 UUID 虽然随机,但如果有人恶意爬取数据,还是可以通过统计分析推测出一些信息。自增 ID 虽然会暴露数据量,但可以通过一些策略来避免,比如对外部接口返回的 ID 进行加密或混淆。

误区二:雪花 ID 是分布式系统的唯一选择

虽然雪花 ID 在分布式系统中表现不错,但并不是唯一的选择。号段模式、有序 UUID 等方案同样可以满足需求,而且在某些场景下表现更好。比如在对性能要求极高的场景下,号段模式可能更合适;在对唯一性要求极高的场景下,有序 UUID 可能更合适。

误区三:自增 ID 无法用于分布式系统

虽然自增 ID 在单机环境下无法保证全局唯一性,但可以通过一些策略来扩展。比如在分库分表时,为每个数据库实例分配不同的自增起始值和步长。比如实例 1 的起始值是 1,步长是 2;实例 2 的起始值是 2,步长是 2。这样两个实例生成的 ID 就不会冲突。不过这种方法在扩容时需要手动调整,比较麻烦。

七、总结:MySQL 的 “主键哲学”

MySQL 不推荐使用 UUID 和雪花 ID 作为主键,核心原因在于性能和存储空间的权衡。UUID 和雪花 ID 虽然在分布式系统中具有全局唯一性和有序性的优势,但它们的随机插入和较大的存储空间会导致索引效率低下、页分裂频繁,进而影响整体性能。而自增 ID 虽然在分布式系统中存在局限性,但在单机环境下表现出色,是 MySQL 的 “最优解”。

那么,在实际项目中该如何选择主键呢?咱们可以遵循以下原则:

  • 单机系统:优先选择自增 ID,性能和存储空间都有保障。
  • 分布式系统:根据业务需求选择合适的方案。如果对性能要求极高,可以选择号段模式;如果对唯一性要求极高,可以选择雪花算法或有序 UUID。
  • 特殊场景:如果业务需要暴露 ID 给外部,或者对安全性要求极高,可以考虑对 ID 进行加密或混淆。

本文地址:https://www.yitenyun.com/343.html

搜索文章

Tags

数据库 API FastAPI Calcite 电商系统 MySQL Web 应用 异步数据库 数据同步 ACK 双主架构 循环复制 Deepseek 宝塔面板 Linux宝塔 Docker 生命周期 JumpServer JumpServer安装 堡垒机安装 Linux安装JumpServer 序列 核心机制 esxi esxi6 root密码不对 无法登录 web无法登录 SSL 堡垒机 跳板机 HTTPS Windows Windows server net3.5 .NET 安装出错 宝塔面板打不开 宝塔面板无法访问 查看硬件 Linux查看硬件 Linux查看CPU Linux查看内存 HTTPS加密 连接控制 机制 Windows宝塔 Mysql重置密码 无法访问宝塔面板 Oracle 处理机制 HexHub 运维 Serverless 无服务器 语言 Spring Redis 异步化 ES 协同 技术 group by 索引 InnoDB 数据库锁 监控 开源 PostgreSQL 存储引擎 分页查询 高可用 SQL 动态查询 缓存方案 缓存架构 缓存穿透 响应模型 自定义序列化 R edis 线程 数据 主库 服务器 管理口 scp Linux的scp怎么用 scp上传 scp下载 scp命令 日志文件 MIXED 3 查询 SVM Embedding 存储 OB 单机版 Linux 安全 工具 电商 系统 SQLark SQLite-Web SQLite 数据库管理工具 架构 Rsync Postgres OTel Iceberg 云原生 RocketMQ 长轮询 配置 Recursive 共享锁 流量 • 索引 • 数据库 防火墙 黑客 修改DNS Centos7如何修改DNS 向量数据库 大模型 聚簇 非聚簇 sftp 服务器 参数 PG DBA 优化 万能公式 缓存 业务 AI 助手 Ftp ​Redis 机器学习 推荐模型 场景 GreatSQL 连接数 Netstat Linux 服务器 端口 数据备份 MySQL 9.3 RDB AOF openHalo 数据分类 加密 同城 双活 信息化 智能运维 INSERT COMPACT 窗口 函数 人工智能 推荐系统 核心架构 订阅机制 线上 库存 预扣 事务 Java 开发 MVCC 磁盘架构 redo log 重做日志 向量库 Milvus mini-redis INCR指令 网络架构 网络配置 Doris SeaTunnel prometheus Alert Redisson 锁芯 高效统计 今天这篇文章就跟大家 Undo Log Python B+Tree ID 字段 模型 引擎 性能 网络故障 PostGIS IT运维 Canal Web Redis 8.0 不宕机 数据脱敏 加密算法 R2DBC 分布式 集中式 崖山 新版本 Hash 字段 自动重启 Pottery 虚拟服务器 虚拟机 内存 OAuth2 Token Entity JOIN ZODB 微软 SQL Server AI功能 容器化 MongoDB 数据结构 DBMS 管理系统 容器 数据类型 启动故障 读写 sqlmock LRU SpringAI 悲观锁 乐观锁 分库 分表 QPS 高并发 传统数据库 向量化 数据页 StarRocks 数据仓库 数据集成工具 分页 单点故障 工具链 Testcloud 云端自动化 Redka 分页方案 排版 filelock 排行榜 排序 发件箱模式 事务隔离 部署 聚簇索引 非聚簇索引 1 大表 业务场景 意向锁 记录锁 速度 服务器中毒 Web 接口 SSH 日志 原子性 池化技术 连接池 Caffeine CP 仪表盘 分布式架构 分布式锁​ dbt 数据转换工具 EasyExcel MySQL8 AIOPS MCP 开放协议 优化器 网络 InfluxDB RAG HelixDB Order IT 双引擎 频繁 Codis Go 数据库迁移 字典 Ansible 单线程 Crash 代码 对象 LLM 订单 线程安全 事务同步 Pump List 类型 UUIDv7 主键 服务器性能 锁机制 行业 趋势 TIME_WAIT 负载均衡 分布式锁 CAS 播客 失效 MGR 多线程 技巧 Weaviate Zookeeper 分布式集群 解锁 调优 并发控制 恢复机制 ReadView 闪回 国产数据库 产业链 兼容性 Next-Key 国产 用户 快照读 当前读 视图 RR 互联网 数据字典 算法 主从复制 代理 GitHub Git 语句 UUID ID 神经系统 慢SQL优化 千万级 拦截器 动态代理 Valkey Valkey8.0 矢量存储 数据库类型 AI代理 查询规划 关系数据库 编程 count(*) count(主键) 行数 表空间 恢复数据