开发者工具 · UUID 生成器
随机 UUID 主键和 B 树碎片:到底发生了什么
· 为什么它很重要
uuid 密码学 浏览器 API
随机 v4 键插入随机索引页,而聚集索引为此付出了代价。这篇文章解释了这一机制、文本与二进制的存储成本,以及按时间排序的 UUID 在哪些方面改变了情况。
随着表的增长,插入速度变慢——这是 DBA 需要考虑他们的关键选择的症状
在具有聚集索引的数据库中插入以随机 UUID 作为主键的行会导致数据库在 B 树结构内的随机位置插入新行。顺序键附加到最右边的叶页,将所有插入保留在一个小的缓存驻留区域内。随机键将插入分散到整个索引中,迫使数据库遍历并修改物理存储中相距较远的页面。随着表的增长和树的加深,每次插入都会涉及更多的页面并导致更多的 I/O 操作。在一千行时看似便宜的操作在百万行时变得昂贵。这不是一个理论问题;而是一个问题。它表现为插入吞吐量的可测量下降。
聚簇 B 树如何填充 — 顺序键附加到最后一页;随机键触摸整个索引中的页面
该机制是 B 树工作原理的基础,因为它们在叶页中维护排序的键顺序。当您将带有键 10,001 的行插入到已存储键 1 到 10,000 的表中时,数据库知道该行所属的位置:在末尾,在现有的最右侧页(如果有空间)中,或者在附加到右侧的新页中。当您将具有随机 UUID (如 7524fae2-7dec-11d0-a765-00a0c91e6bf6)的行插入到同一个表中时,数据库必须导航树以查找包含该 UUID 范围内的键的叶页,找到该页内的确切位置并插入该行。如果该页面已满,它将分裂,将其一半内容移动到新页面并更新父节点。
页面拆分和缓存压力 - 为什么随机插入成本更高 I/O 以及为什么效果随着表大小而增加
随机键创建最坏情况的插入模式,因为每个插入都会落在树中的随机位置,而不是附加到最右边的页面。数据库必须搜索正确的页面,这通常需要多次页面读取——树的每一层一次。然后它必须修改该页面,这可能会触发沿树向上传播的分裂。更多页面被修改,更多写入发生,并且内存缓冲池填充来自树的不同区域的页面,而不是专注于活动插入点。缓存未命中增加,I/O 成为瓶颈,随着表规模增大,插入率趋于稳定。
文本或二进制 — 36 字符串与 16 字节本机 uuid 或二进制 (16) 列以及对索引大小的影响
不同数据库引擎的成本并不统一,因为不同的系统优化页面管理的方式不同。具有积极压缩、小页面大小或内存操作的数据库可能会显示顺序键和随机键之间较小的性能差异。具有大页面、机械磁盘或严格内存限制的数据库将表现出急剧的性能下降。该问题是可观察和可测量的:测量 1,000 行、100,000 行和 1,000,000 行的插入率。如果每秒的速率在较大范围内急剧下降,则您将遇到特定硬件和数据库配置的随机插入损失。
按时间排序的替代方案 — UUIDv7 和 ULID 在索引末尾插入时如何保持唯一性
UUID 作为字符串使用文本形式的 36 字符或作为二进制 UUID 类型的 16 字节,具体取决于所选的存储格式。 UTF-8 或 ASCII 列中的 36 字符字符串为 36 字节,而 64 位整数为 8 字节。假设不应用压缩技术,字符串 UUID 列上的索引比整数列上的索引大三倍。较大的索引意味着缓冲池中适合的索引页较少,这意味着遍历树时的缓存命中较少。适合 RAM 的较小索引比每次查询时都必须从磁盘读取的大索引性能更好,无论插入顺序或工作负载模式如何。
工作示例 - 为随机键和时间排序键表描述的相同插入工作负载,定性地,没有发明基准
存储差异对于主索引和辅助索引都很重要,因为包含主键的每个辅助索引都必须存储完整的 36 字符 UUID 或 16 字节二进制 UUID 值。这使得二级索引比使用整数键进行主键查找的索引大得多。复制、备份和查询结果集都随着索引大小的增大而按比例增长。 ToolAcre UUID 生成器生成二进制兼容的值;与 varchar(36) 相比,根据数据库将它们存储为二进制 (16) 或 GUID 类型可以节省空间,并全面提高缓存效率。这种存储优化对于大型系统至关重要。
这不包括什么——堆组织的表和数据库,其中主键不是聚集的,影响较小
一种常见的优化是将 UUID 在内部存储为二进制文件,并仅在 API 或用户界面需要时将其显示为字符串。索引和连接操作以紧凑的二进制形式进行; API 响应或应用程序代码转换为字符串表示形式。某些数据库提供内置 GUID 或 UUID 类型来自动处理此转换。其他则需要显式的转换操作。 36 字节和 16 字节列之间的性能差异是真实存在的:具有一百万行和 36 字节的表与 16 字节 UUID 列键在每个索引级别上相差 20 MB,这可能是适合 L3 缓存的索引与需要内存提取之间的差异。
要点:在选择版本之前了解您的索引 — ToolAcre 生成器会生成随机 UUID;使用这篇文章来决定是否适合您的存储引擎
插入性能、索引大小和查询特征之间的权衡需要基于工作负载模式的架构决策。如果字符串是升序的(例如基于时间戳的字符串),则顺序字符串键可以快速插入,但会消耗与随机 UUID 相同的空间并泄漏时间信息。随机 UUID 在语义上更干净,并且没有可泄漏的时间戳组件,但插入聚集索引的速度较慢,并且总体存储空间较大。像 UUIDv7 这样的时间排序替代方案通过保持插入局部性同时避免版本 4 随机标识符中的时间戳泄漏来结合优点。