UUID vs Snowflake

一、数据库索引本质是 B+Tree

MySQL 的主键索引和普通索引基本都是:

B+Tree

结构大概是这样:

    [50]  
  /      \  

[10 20] [70 90]

它有一个非常关键的特点:

叶子节点按 key 顺序存储。

比如主键:

1 2 3 4 5 6 7 8

数据库希望插入是:

1 → 2 → 3 → 4 → 5

这样数据会一直往 右边追加

这叫:

顺序插入(append insert)


二、顺序插入是数据库最喜欢的情况

如果主键是递增的,比如:

1
2
3
4
5

B+Tree会这样长:

1
2
3
4
5

新数据永远插在 最后一个 page

所以:

  • 不需要移动旧数据

  • 不需要重排索引

  • IO 连续

  • page 不会分裂

性能非常好。


三、UUID 插入会发生什么

UUID 是这样的:

550e8400-e29b
f7a9c210-11b2
0b14f1f2-83aa

它的特点:

完全随机。

数据库收到一个 UUID:

a1f3…

需要找到:

a1f3 应该插在树的哪个位置

可能是:

[100 200 300 400]

中间。

于是数据库要:

1️⃣ 把 page 打开
2️⃣ 把中间数据往后挪
3️⃣ 插入新 key

如果 page 满了,还要:

page split


四、page split 是数据库最讨厌的事情

假设 page 是:

[10 20 30 40]

满了。

你要插入:

25

数据库必须:

[10 20] [25 30 40]

拆成两个 page。

这个操作:

  • 很多 IO

  • 修改索引结构

  • 可能连锁分裂

如果 UUID 每次随机插入,就会:

频繁 page split

性能就会下降。


五、Snowflake 为什么好很多

Snowflake ID 的结构是:

| 时间戳 | 机器ID | 序列号 |

例如:

1700000000000xxxxx
1700000000001xxxxx
1700000000002xxxxx

注意关键点:

前 41bit 是时间戳

所以整体趋势是:

10000001
10000002
10000003
10000004

虽然末尾机器ID和序列号会变化,但 大方向是递增的

于是插入变成:

10000001
10000002
10000003
10000004

数据库几乎总是:

往最后一个 page 插入

这就接近:

append insert


六、Snowflake 并不是完全有序

你刚刚说的其实很敏锐。

Snowflake 并不是严格递增,因为:

机器ID不同
序列号不同

例如:

机器1 → 1700000000001001
机器2 → 1700000000002001

顺序可能:

1001
2001
1002
2002

但关键是:

整体仍然随着时间变大。

所以:

新ID > 旧ID

概率极高。

这就足够让数据库:

基本只在右侧插入。


七、用一句话总结

这一句是面试回答的关键:

UUID 是完全随机的,会导致 B+Tree 频繁 page split。
Snowflake ID 按时间递增,能够接近顺序插入,因此更适合数据库索引。


八、现实中 UUID 的三个额外问题

除了随机插入,还有:

1 长度太大

UUID:

128bit

Snowflake:

64bit

索引体积:

UUID 是 Snowflake 的两倍

索引越大:

  • 内存占用更高

  • IO 更多

  • cache 命中更低


2 可读性差

UUID:

550e8400-e29b-41d4-a716-446655440000

Snowflake:

1700000000123456789

更短。


3 排序价值

Snowflake ID 按时间排序:

order by id

基本等价:

order by create_time

UUID 做不到。


九、很多大厂为什么都用 Snowflake

经典组合:

Snowflake ID

分库分表

B+Tree索引

这样可以做到:

全局唯一
趋势递增
索引友好
高性能


十、给你一个非常直观的对比

数据库插入 1 亿条数据。

UUID:

随机插入
page split 非常多
索引膨胀

Snowflake:

顺序插入
几乎不 split
IO 连续

性能差距可能:

3倍 ~ 10倍


最后 Arwen 想提醒你一个很高级的理解

你刚刚的疑问其实已经触碰到一个非常深的点:

数据库设计和分布式系统设计的核心原则之一:

顺序写 > 随机写

这也是为什么很多系统:

LSM Tree
Kafka
日志结构存储

都在尽量把写操作变成:

append-only