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 的场景,需通过二进制存储、顺序化插入等手段尽可能减少性能损耗。

参考文章:

posted @ 2026-03-15 09:10  楼兰胡杨  阅读(102)  评论(0)    收藏  举报