为什么MySQL不建议使用UUID做主键_分析B+树索引的页分裂问题

小雪酱_7262

小雪酱_7262

2026-05-18

183人浏览

原创

uuid插入导致b+树频繁页分裂,因其随机性使新记录常插入页中间,触发innodb页分裂、增加随机i/o和索引碎片;即便转为binary(16)或mysql 8.0+的uuid_to_bin(uuid,1),若未重排字节序仍难改善。

为什么mysql不建议使用uuid做主键_分析b+树索引的页分裂问题

UUID插入导致B+树频繁页分裂

MySQL InnoDB用主键构建聚簇索引,数据行直接存放在B+树叶子节点里。页大小固定为16KB,当一页快满时,新记录必须“找位置”插入——自增ID总追加到末尾,而UUID是完全随机的字符串,每次插入都可能落在任意页的中间位置。

结果就是:原本填满80%的页,因一个UUID插进来触发Page Split(页分裂),InnoDB被迫把原页约一半数据挪到新页,再调整父节点指针。这个过程不仅耗CPU,还产生大量随机磁盘I/O。

  • 实测100万行后,每插入1条UUID主键记录,平均引发0.3次页分裂;自增ID则基本为0
  • 页分裂后,原页利用率常跌至50%以下,形成索引碎片,SHOW TABLE STATUS里Data_free值会明显升高
  • 分裂不是孤立事件:父节点也可能因子节点增多而分裂,形成级联效应

为什么字符串UUID比BINARY(16)更糟

很多人以为把UUID存成BINARY(16)就能解决性能问题,其实只是缓解了存储开销,没动根本——无序性还在。

UUID作为字符串(如'550e8400-e29b-41d4-a716-446655440000')存入CHAR(36)或VARCHAR(36),比较逻辑是字典序,B+树内部节点排序、范围扫描全受拖累;换成BINARY(16)后,虽然比较变快了,但二进制值仍是时间无关的随机分布,页分裂照旧。

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载
  • CHAR(36)占36字节,BINARY(16)占16字节,但两者在B+树中插入位置的随机性一致
  • 二级索引叶子节点存的是主键值,BINARY(16)让二级索引体积缩小约55%,但回表时仍要按随机地址读取数据页
  • 真正起作用的是顺序性,不是字节长度——所以UUID_TO_BIN()本身不解决问题,必须配合ORDER BY重排(MySQL 8.0+支持)

有序UUID能彻底避免页分裂吗

MySQL 8.0引入UUID_TO_BIN(uuid, true),把时间戳高位移到前缀,生成“时间有序”的二进制UUID。这确实大幅减少页分裂,但不是零分裂。

因为UUIDv1/v6的时间精度是100纳秒,同一毫秒内生成多个UUID时,后缀(clock sequence + node ID)仍是随机的。如果单机高并发写入(比如Web服务器每毫秒生成上百ID),这些“同毫秒ID”依然会集中插入到同一数据页,撑满后照样分裂。

  • 测试显示:有序UUID在10万TPS写入下,页分裂率比普通UUID低90%,但仍比自增ID高3~5倍
  • 它对范围查询有帮助(比如WHERE id > BIN_TO_UUID(...)可走索引),但无法像自增ID那样保证物理连续
  • 依赖MySQL 8.0+,低版本无法用;且应用层需统一调用UUID_TO_BIN(),否则混用会导致索引乱序

自增ID的“顺序性”本质是什么

关键不在“自增”,而在“单调递增+无间隙预期”。InnoDB只要确认下一个id一定大于所有现存值,就能把新行无条件追加到B+树最右叶子节点末尾。

这个行为带来三个硬性优势:页填充率高(常达15/16)、无需查找插入点(省掉树遍历)、磁盘写入是顺序IO(OS预读生效)。而雪花ID虽是LONG型且含时间戳,但工作节点ID和序列号组合后,跨机器写入时ID会跳跃(比如节点A刚写完123456789012345678,节点B下一条可能是123456789098765432),这种跳跃仍会引发少量页分裂。

  • 真正安全的“顺序”只存在于单点生成场景;分布式ID必须接受轻微分裂代价
  • 如果你的业务允许主键暴露增长趋势(比如订单号被用户看到),自增ID仍是首选
  • 若必须全局唯一,优先考虑带时间前缀的雪花ID,而非UUID——至少它不拖慢字符串比较
页分裂不是黑盒警告,它是B+树面对随机写入时的必然反应。判断要不要用UUID做主键,先看你的写入QPS和单日增量:如果峰值写入<1000 TPS、日增<10万行,优化空间远不如先调好innodb_buffer_pool_size和innodb_log_file_size来得实在。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

mysql

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4223

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

891

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1089

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6121

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2983

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6080

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

8201

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1110

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

972

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 183人学习