MySQL不推荐使用UUID等字符串做主键

摘要:MySQL InnoDB表不推荐使用UUID等字符串做主键,推荐用整型自增ID。

  先说简单描述一下结论:使用UUID(通用唯一识别码)等字符串作为主键会因乱序插入而频繁触发页分裂,所以,尽量选择整型连续自增ID作为索引,尤其是主键索引。

  在 MySQL 表设计中,主键数据结构的技术选型是影响性能的关键因素。雪花 ID(Snowflake)和 UUID 作为分布式系统中常用的唯一标识符,虽能解决全局唯一性问题,但在 MySQL InnoDB存储引擎中却并非主键的最佳选择。本文将从 MySQL 索引机制、数据存储原理、性能影响等角度,解析为何不推荐使用字符串作为主键。

  你如果不了解MySQL索引中数据的存储机制、查询过程和插入过程,请参考《浅析二叉树、B树、B+树、B*树和MySQL索引底层原理》,它介绍了二叉树、AVL树、红黑树、B树、B+树和B*树等六种树的数据结构后,浅析MySQL InnoDB索引选择B+树的原因,并给出一些 MySQL 索引高频面试题。这些知识点对回答上述问题非常非常重要,我简明扼要的归纳与本文密切相关的以下几点索引机制:

  • B+树是一种多路平衡搜索树
  • MySQL InnoDB存储引擎的索引在B+树上维护表数据
  • B+树上的每个节点在计算机中叫做数据页,默认大小为16KB
  • 主键索引的叶子节点存储数据行,内部节点只存储用于路由的主键
  • 辅助索引的叶子节点存储主键而非数据行,需通过回表查询数据行
  • 主键索引的叶子通过双向链表链接,其数据行按主键值升序排列,提升范围查询性能
  1. 存储空间开销大,浪费磁盘

    UUID包含36个字符,即便存储为BINARY(16)也占用16字节的空间,比整数主键(例如bigint类型,占用8个字节)占用更多的存储空间,其它业务字符串存在同样的问题。虽然对于单个表的影响可能不大,但在大型数据库中,这种额外的空间占用会显著增加存储成本。由此带来的连锁消极影响:辅助索引的叶子节点存储了主键值,进一步导致索引体积膨胀,内存中可缓存的索引数据减少,I/O压力增大。

  2. 字符串比较效率低

    按主键范围查询时(如WHERE id > 100),自增ID的数据逻辑连续,可根据叶子节点之间的双向链表高效顺序读取;UUID等字符串随机分散存储,需跨多个页随机读取,而且比较时涉及字符编码和排序规则,故查询效率低,缓存命中率低。

  3. 无序性导致索引碎片化,造成写入性能差

    InnoDB自增ID保证新增数据自动追加到索引末尾,而字符串主键乱序分布,容易频繁触发页分裂和磁盘随机I/O,即便使用有序的ULID字符串主键也会导致数据页填充率低,产生更多碎片,需频繁执行OPTIMIZE TABLE维护,影响稳定性,显著降低写入速度,增加开销。

  4. 适用场景与替代方案

    若无分布式需求,优先使用自增整型(BIGINT)作为主键;若在高并发分布式系统中则选择UUID等字符串ID。优化方案:使用有序ULID,或者采用雪花算法等生成不连续的、单调递增的唯一ID;在需要将业务键(如电子邮件地址或用户名)用作主键时,可以使用一个隐藏的自增ID作为技术主键(代理键),而将业务键用于查询和显示;频繁删除操作可能导致页面过于稀疏,从而触发叶合并。所以,一般建议使用逻辑删除而不是物理删除。

  总之,小编楼兰胡杨认为MySQL 的主键设计本质上是在数据唯一性、查询性能、存储效率之间的权衡。除非有明确的分布式唯一性诉求,否则,使用整型自增ID作为主键是更优秀的选择。对于必须使用雪花 ID 或 UUID 的场景,需通过二进制存储、顺序化插入等手段尽可能减少性能损耗。

参考文章:

相关推荐
程序员-Benothing1 分钟前
MySQL 二级索引有 MVCC 快照吗?
数据库·mysql
Java.熵减码农3 小时前
MySQL 远程连接故障排查全记录(Linux + Docker + Navicat)
linux·mysql·docker
拾光Ծ4 小时前
【MySQL】基础入门:库操库、数据表操作与数据类型详解
数据库·sql·mysql·数据类型·库操作·表操作
夜雪一千4 小时前
MySQL Insert Intention Lock 插入意向锁是什么
数据库·mysql
Nontee13 小时前
MySQL 插入冲突了怎么办?两种处理方式入门笔记
数据库·笔记·mysql
devilnumber15 小时前
MySQL 性能优化・诗意化记忆
数据库·mysql·性能优化
宠友信息17 小时前
社区类源码开发实践中的仿小红书系统技术要点分析
java·spring boot·redis·mysql·uni-app·vue·内容运营
Lyra_Infra18 小时前
MySQL Docker 误删恢复:binlog PITR
mysql·docker·命令行
南城以南溫暖如初14718 小时前
从零搭建24小时自助健身系统:技术选型与核心模块实战
java·spring boot·redis·mysql·vue·mybatis