文章摘要
文章建议避免使用UUID版本4作为主键,因其随机性会导致数据库索引碎片化、插入延迟和存储空间浪费,并指出UUID并不安全,推荐使用整数等更高效方案。
文章总结
标题:避免使用UUID版本4作为主键
文章来源:https://andyatkinson.com/avoid-uuid-version-4-primary-keys
发布时间:2025年7月2日
主要内容概述:
作者基于多年数据库工作经验,指出使用UUID版本4(UUID v4)作为主键数据类型会导致数据库性能下降和I/O负载过高的问题。文章详细分析了UUID v4的缺陷,并提出了替代方案。
核心问题:
1. 随机性缺陷:UUID v4的122位随机比特导致索引插入和查询效率低下,无法像整数或有序字符串那样自然排序。
2. 空间占用:16字节的UUID是8字节bigint的两倍,4字节整数的四倍,随着数据量增长会显著增加存储压力。
3. 性能影响:随机值导致B树索引频繁发生页分裂和碎片化,增加写入延迟(如Buildkite案例显示改用时间有序UUID后WAL写入I/O减少50%)。
测试数据对比:
- 在1000万行数据上,UUID v4索引扫描需要访问856万个缓冲区,而bigint仅需2.7万个,前者多消耗约68GB内存数据,导致查询延迟增加0.86~3.4秒。
- 页填充率测试显示:整型索引达97.64%,UUID v7为90.09%,而UUID v4仅79.06%,表明后者存储密度更低。
常见误区:
- 安全性误解:RFC 4122明确指出UUID不应被视作安全凭证。
- 替代方案缺失:实际上可通过整数生成混淆码(如XOR加密+Base62编码)实现类似UUID的不可猜测性。
解决方案建议:
1. 优先选择有序类型:
- 新项目推荐使用自增整型(identity列)或bigint。
- 必须使用UUID时,选择含时间戳的UUID v7(PostgreSQL 18原生支持,当前可通过pguuidv7扩展实现)。
2. 优化现有系统:
- 定期重建索引(REINDEX CONCURRENTLY)或表(pgrepack)。
- 增加workmem配置提升排序性能。
- Rails应用可通过implicitorder_column指定时间戳字段替代默认排序。
总结:
UUID v4因随机性导致显著的性能损耗,在OLTP场景中应避免作为主键。对于需要分布式ID生成的场景,时间有序的UUID v7或整数派生编码是更优选择。
延伸阅读:
- PostgreSQL的I/O基础(pganalyze)
- 序列与UUID的对比(Brandur Leach)
- UUID v7技术解析(The Nile Blog)
(注:原文中的技术细节如RFC引用、扩展模块链接等已精简,保留核心论点和数据支撑。)
评论总结
以下是评论内容的总结:
支持UUIDv7的观点
性能优势:UUIDv7相比UUIDv4在PostgreSQL中有更好的性能表现,适合中小型数据库。
- "UUIDv7 still seems like a reasonable compromise for small-to-medium databases." (benterix)
- "in PG, prefer using UUIDv7 over UUIDv4 as they have slightly better performance." (xandrius)
PostgreSQL支持:最新版本的PostgreSQL已原生支持UUIDv7。
- "Postgresql 18 released in September and has uuidv7" (mcny)
反对UUIDv7的观点
过早优化:UUIDv4在许多场景下已经足够,性能优化应基于实际需求。
- "UUIDv4 is perfectly fine for many use cases, including small databases." (ivan_gammel)
- "A prime example of premature optimization." (vintermann)
复杂性增加:UUIDv7引入了时间戳依赖和额外复杂性,可能不适合嵌入式系统等场景。
- "UUIDv7 is not simple; it's very complicated." (socketcluster)
- "The whole point of UUID was to leverage the properties of randomness... UUIDv7 seems to take things in a philosophically different path." (socketcluster)
替代方案建议
整数PK + UUID逻辑键:建议使用整数作为主键,UUID作为逻辑键用于导入/导出。
- "why no use ints for PK, and UUIDs for a public_id field?" (dimitrisnl)
- "start with big-ints and add a GUID code field if it becomes necessary." (BartjeD)
加密混淆整数:如果需要隐藏整数ID,可以使用加密方法而非简单XOR。
- "Use a block cipher in ECB mode... do not even think about using RC4." (dfox)
其他讨论
分布式数据库场景:在分布式数据库中,顺序键可能导致热点问题,UUID可能是更好的选择。
- "Sequential key would be atrocious in such circumstance." (kaladin_1)
安全性问题:UUID不应作为安全凭证,但实际使用中并未因此导致严重问题。
- "I have seen UUIDs for this in practice though and these systems weren’t compromised because of that." (raxxorraxor)
过度设计警告:开发者应避免过度复杂化设计,简单方案往往更有效。
- "Why do we developers like to overcomplicate things?" (grugdev42)
关键引用
性能与实用性:
- "UUIDv7 still seems like a reasonable compromise for small-to-medium databases." (benterix)
- "UUIDv4 is perfectly fine for many use cases, including small databases." (ivan_gammel)
复杂性批评:
- "UUIDv7 is not simple; it's very complicated." (socketcluster)
- "A prime example of premature optimization." (vintermann)
替代方案:
- "why no use ints for PK, and UUIDs for a public_id field?" (dimitrisnl)
- "Use a block cipher in ECB mode... do not even think about using RC4." (dfox)