以下是关于为什么不推荐使用 UUID 作为主键的详细说明和例子:
1. 性能问题
原因:
UUID 通常为 128 位的数据,如果以字符串形式存储(如 char(36)
或 varchar(36)
),会占用大量存储空间。相较之下,自增的整数主键通常是 4 字节(INT)或 8 字节(BIGINT),空间更小且效率更高。此外,UUID 的随机性会影响数据库的缓存命中率,从而降低查询和写入性能。
举例:
- UUID: 插入数据时,生成的 UUID 是随机的,如
550e8400-e29b-41d4-a716-446655440000
,它可能被插入到索引的任意位置,这会导致索引频繁重组。 - 自增整数: 使用自增 ID,主键的值是连续增长的,例如
1, 2, 3,...
,新数据会按顺序插入索引的末尾,索引维护更高效,写入性能更优。
2. 可读性差
原因:
UUID 是随机的长字符串,不容易通过人工快速识别或记忆。相比之下,数字主键通常简单易懂,有助于调试和问题排查。
举例:
- UUID: 日志中记录一条错误信息时,主键为
f47ac10b-58cc-4372-a567-0e02b2c3d479
。开发者在排查问题时,必须复制完整的 UUID,而无法快速记忆或手动输入。 - 自增整数: 如果主键是
1024
,开发者可以轻松记住并输入到查询语句中,例如SELECT * FROM orders WHERE id = 1024;
。
3. 索引碎片化
原因:
UUID 的随机性会导致数据插入索引时,索引树(如 B+ 树)出现较多的非顺序写入。这会增加索引碎片化的可能性,降低索引查询性能。
举例:
- UUID: 数据库中现有记录主键为
0001xxxx
,0002xxxx
,0003xxxx
。插入新记录a1b2xxxx
,由于新主键值的随机性,它会被插入到索引树的中间部分,导致索引重新平衡。 - 自增整数: 主键为
1000, 1001, 1002,...
的表中插入新记录1003
,只需要在索引末尾追加,索引无需重组。
4. 复杂性
原因:
UUID 的生成需要依赖算法(如版本 1 或版本 4 的 UUID 生成器),并可能要求系统时间或随机数生成器的支持。对于跨系统或分布式场景,还需要额外的配置以避免冲突,而自增主键则更简单。
举例:
- UUID: 在分布式系统中,不同节点可能生成重复的 UUID,必须依赖高质量的 UUID 生成工具来避免冲突。此外,某些 UUID 生成方式(如基于时间的版本 1)可能会暴露系统信息(如时间戳或网卡地址),带来安全隐患。
- 自增整数: 即使在分布式场景中,可以为每个节点分配不同的 ID 段(如节点 1 使用
1-1000
,节点 2 使用1001-2000
),简单易行。
总结:
虽然 UUID 的全局唯一性在某些场景(如分布式系统或数据同步)下很有用,但因其性能和复杂性问题,建议谨慎选择。若一定要用 UUID,可以考虑优化策略,例如:
- 使用更紧凑的存储方式(如
binary(16)
)。 - 配合其他索引或策略(如分区表)来降低性能影响。