Hollis Varden:以 text 存储 UUID:在 PostgreSQL 18 中的代价

💡 原文英文,约700词,阅读约需3分钟。
📝

内容提要

PostgreSQL 18 测试表明,将 UUID 主键存为 text 类型代价显著:500 万行数据下,索引体积大 88%,B 树多一层,每次随机查询多读一页;插入更慢,UUIDv4 因随机写入多耗 33 秒和 540MB WAL。原因是 text 占 37 字节,远大于 uuid 的 16 字节。建议使用 uuid 类型。

🔎

延伸解读

text 存储 UUID 的代价根源

文章指出,UUID 以 text 存储时占 37 字节(36 字符加长度字节),而 uuid 类型仅 16 字节。索引条目包含键值,因此 text 索引的键数据量是 uuid 的两倍多,导致索引体积增大 88% 至 89%,并可能使 B 树多一层。每次随机查询因此多读一页,表本身也因堆存储更长键而增大。

随机写入放大 WAL 开销

测试显示,UUIDv4 随机写入时,text 类型比 uuid 多耗 33 秒插入时间,并多写 540MB WAL;UUIDv7 的差距较小,为 6 秒和 247MB。作者推测,更大的索引在随机插入时更易触发页分裂,并写入更多全页镜像到 WAL。这表明 text 存储会放大随机写入的代价。

未覆盖的变量与迁移注意

文章未测试非 C 排序规则、text 键的连接与外键、超出内存的数据集、索引层级相同的小表以及其他硬件。作者提醒,若已有 text 键,改为 uuid 需执行 ALTER TABLE ... TYPE uuid USING id::uuid,会重写表和索引并持有强锁,且未测量其影响。

❓

Q&A

在 PostgreSQL 18 中,把 UUID 主键存为 text 类型,索引会变大多少?

在 500 万行数据下,text 类型的 UUID 主键索引比 uuid 类型大 88%(UUIDv7)到 89%(UUIDv4)。

为什么用 text 存储 UUID 会导致索引更大?

因为 uuid 类型只占 16 字节,而 text 类型需要 36 个字符加上 1 个长度字节,共 37 字节。每个索引条目都包含键值,所以 text 索引的键数据量是 uuid 的两倍多。

text 存储 UUID 对查询性能有什么影响?

text 索引的 B 树层级比 uuid 索引多一层(层级 3 对 2),因此每次随机查询需要多读一页。在 10,000 次随机查询中,text 索引多读了约 10,400 个缓冲区。

用 text 存储 UUID 对插入速度和 WAL 生成有什么影响?

插入更慢,且 WAL 生成更多。对于 UUIDv4,text 类型比 uuid 类型多耗时 33 秒,多生成 540 MB WAL;对于 UUIDv7,多耗时 6 秒,多生成 247 MB WAL。

在 PostgreSQL 中,应该用 uuid 还是 text 类型存储 UUID 主键?

建议使用 uuid 类型。因为 text 类型会导致索引更大、B 树更深、查询和插入更慢,且 WAL 生成更多。

如果已经用 text 存储了 UUID,如何改成 uuid 类型?

可以使用 ALTER TABLE ... TYPE uuid USING id::uuid 来修改列类型,但这会重写表和索引,并在执行期间持有强锁。

🏷️

标签

➡️

继续阅读