ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【高频面试题】为什么不建议用uuid当主键(附B+树原理详解)

【高频面试题】为什么不建议用uuid当主键(附B+树原理详解) 前提数据库MySQL InnoDB 索引最核心的数据结构是B 树。为什么不能用uuid当主键而要用有序的idInnoDB 的主键是聚簇索引整张表的数据全部存在主键 B 树的叶子节点。叶子节点的数据是按主键有序排列的叶子节点之间靠双向链表串起来。节点有容量上限我们例子叶子最多存 3 条记录装满就要分裂。我们先记住两个核心结论后面用例子推演雪花 ID递增有序主键 → 新数据永远往链表最后一个叶子节点追加很少触发节点分裂。UUID随机无序主键 → 新数据随机插入到 B 树中间某个叶子节点极易触发叶子节点分裂性能很差。示例 B 树聚簇索引[13 , 25] / | \ L1 L2 L3 (3,data) (13,data) (25,data) (7,data) (17,data) (30,data) 叶子链表L1 ↔ L2 ↔ L3 叶子最多容纳3条记录。叶子链表顺序就是主键从小到大3,7,13,17,25,30场景 1主键是【有序雪花 ID】趋势递增雪花 ID 特点新生成的 ID 永远比之前所有 ID 更大。 现在持续插入新数据 已有最大主键 30新插入 ID31。查找位置根节点[13,25]31≥25 →走到最右边叶子 L3L3 现在有两条记录25、30还能再塞 1 条上限 3直接追加到 L3 里面L3 变成(25,30,31)✅不需要分裂节点再来插入 ID32 L3 已经满3 条记录放不下。触发叶子节点分裂L3 旧25,30,31分裂旧 L3 保留前一半25,30新建叶子 L4 放后一半31,32L4 最小 key31把 31 插入到根节点 根从[13,25]→[13,25,31]叶子链表L1 ↔ L2 ↔ L3 ↔ L4 特点插入永远只操作最右侧叶子节点绝大多数情况直接追加只有最右边叶子满了才会分裂。 节点分裂的次数非常少。节点分裂是昂贵操作磁盘写新页、修改上层索引 key、修改链表指针一次分裂多次 IO。场景 2主键是【UUID】UUID随机无序比如550e8400-e29b-41d4-a716-446655440000UUID 新 ID 不一定比旧 ID 大。 现有树[13 , 25] / | \ L1 L2 L3 (3,data) (13,data) (25,data) (7,data) (17,data) (30,data)现在插入一个随机 UUID换算成数字举例15查找位置根节点[13,25]13 ≤1525 →走到中间叶子 L2L2 现在已经有13,17还能塞一条插入 15 → L213,15,17刚好 3 条满了再来插入下一个随机 UUID换算成数字16定位到 L2L2 现在13,15,17已经装满 3 条必须分裂中间这个叶子 L2L2 旧13,15,17分裂旧 L2 保留13,15新叶子 L4 放17,16排序后16,17L4 最小 key16把 16 插入根节点 根变成[13,16,25]链表L1 ↔ L2 ↔ L4 ↔ L3⚠️重点区别雪花 ID 只分裂最右边叶子UUID 会随机在树中间位置的叶子节点插入、触发分裂。继续再来一个随机 UUID9 定位到 L13,7插入 9L1 变成3,7,9满了。再来随机 ID8 →又要分裂左边 L1 节点。UUID 带来的一连串问题基于 B 树原理频繁节点分裂每一次随机插入都有可能命中一个已经快满的叶子触发分裂。大量随机写入分裂次数暴增大量磁盘 IO写入性能暴跌。索引页碎片化叶子节点本来是连续的磁盘页。反复在中间分裂新叶子页会落在磁盘不同位置。 原本连续的叶子链表物理磁盘上变得零散。范围查询的时候磁盘需要大量随机读速度变慢。非叶子节点大量维护开销每次叶子分裂要把新叶子的最小 key 插入上层非叶子节点。随机插入会造成上层节点也频繁分裂B 树整体变高查询时磁盘 IO 次数变多。UUID 本身更长16 字节雪花 ID 一般 8 字节 主键越长非叶子节点能存放的 key 数量越少。同样大小的索引页能存的分界 key 变少 →树高度更高查询 IO 变多。
返回列表