[翻译] UUID 作为 InnoDB 主键的性能影响

2019-12-28 18:42:46 +08:00
 RedisMasterNode

博客完整译文: https://blog.2014bduck.com/archives/287

原文标题:UUIDs are Popular, but Bad for Performance — Let’s Discuss
原文链接: https://www.percona.com/blog/2019/11/22/uuids-are-popular-but-bad-for-performance-lets-discuss/
作者:Yves Trudeau
翻译时间:2019-12-28

UUID 值有什么问题?

InnoDB 将表中的行存储在主键的 B 树中,在数据库中我们称之为聚簇索引。聚簇索引自动将数据行按主键顺序排列。

当你插入一行随机主键值的数据,InnoDB 需要找到这行应该属于哪一页,如果页没在缓冲池中则将其加载进缓冲池,插入数据行,最后将脏页刷回磁盘。纯随机值加上大表使得 B 树上的每个叶子节点都有机会插入行,而没有热点数据页。数据行不按照主键顺序(译注:主键顺序指主键顺序的末端)插入会导致页的分裂,进一步导致页的填充因子降低。在缓冲池中,有新数据插入的页称为脏页。而缓冲池中的页在被刷回磁盘前再次有新数据需要写入的概率很低。所以大部分时间中,每次插入操作会导致两次 IO 过程——一次读取和一次写入。所以首先 UUID 会对 IO 操作的比例造成影响,而这个又是伸缩性的主要限制因素。

硬件上来讲,获得高性能的唯一方法就是使用低延迟和高耐久的存储介质。然而这又是一个对性能造成影响的因素。因为聚簇索引的存在,辅助索引需要使用主键值作为指针。主键 B 树的叶子节点存储数据行,而辅助索引的叶子节点存储主键值。

我们假定有一个 UUID 作主键的表,并且有 5 个辅助索引,一共 10 亿行数据。如果你读了前面的段落,你会知道每行主键值被存了 6 次。也就是说一共 6 亿的 36 字节字符串值,216GB。这只是冰山一角而已,因为表通常还会有外键,显式或者隐式地指向其他表。当表是基于 UUID 设计的时候,列或者索引需要以char(36)来容纳数据。最近我分析了一个基于 UUID 的表,发现 70%的存储空间都用来存放 UUID 值。

不止这些,UUID 还有第三点影响。整型在 CPU 中一次性可以比较 8 字节,而 UUID 是逐字节比较的。数据库很少会受限于 CPU 性能,但是不管怎么样这都会提高查询的延迟。

8561 次点击
所在节点    MySQL
28 条回复
optional
2019-12-29 13:51:31 +08:00
uuid 用 36 字节? 16 字节就够了啊
RedisMasterNode
2019-12-29 15:50:29 +08:00
做了个简单的小测试,乱序 32 位 MD5 ID vs 顺序 32 位字符串整数 ID vs 子增 ID 导致的页分裂次数,50000 条数据 INSERT:

```SQL
mysql> SELECT NAME, COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
LIKE '%split%';
+---------------------------------+-------+
| NAME | COUNT |
+---------------------------------+-------+
| index_page_splits | 406 |
+---------------------------------+-------+
1 rows in set (0.00 sec)

mysql> SELECT NAME, COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME
LIKE '%split%';
+---------------------------------+-------+
| NAME | COUNT |
+---------------------------------+-------+
| index_page_splits | 288 |
+---------------------------------+-------+
1 rows in set (0.00 sec)


mysql> SELECT NAME, COUNT FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE NAME LIKE '%split%';
+---------------------------------+-------+
| NAME | COUNT |
+---------------------------------+-------+
| index_page_splits | 193 |
+---------------------------------+-------+
1 rows in set (0.00 sec)
```
pabno
2019-12-29 17:19:15 +08:00
@wysnylc 可以使用雪花算法
FS1P7dJz
2019-12-29 19:02:40 +08:00
@wysnylc snowflake 了解一下,推特在用的,性能可以百万级
我相信绝大部分同类场景应该都足够满足
lolizeppelin
2019-12-29 22:59:40 +08:00
要加上定语 MYSQL

其他数据库 uuid 似乎没问题哦 哈哈哈
更别说时间序的 uuid 了
RedisMasterNode
2019-12-30 00:05:01 +08:00
@lolizeppelin 为什么,不是特别了解,但是 InnoDB 应该表达得比较明确了个人认为?据我所知 InnoDB 都是 B+树的索引组织表,如果不是的话请举出具体反例
hooopo
2019-12-30 13:09:17 +08:00
单机搞什么 uuid,分布式 uuid 也没用
RedisMasterNode
2019-12-30 14:22:48 +08:00
早知道就把标题改成散列 ID 的影响了...大家反正都不看内容只看标题..

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/633171

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX