为什么MySQL的自增主键不建议使用UUID_防止B+树索引频繁分裂与页偏移

夜芳吖_8309

夜芳吖_8309

2026-05-11

983人浏览

原创

uuid主键触发频繁页分裂,因innodb聚簇索引按主键物理排序,而uuid()生成的随机128位字符串插入时大概率落在b+树中间页,页满即分裂,导致i/o放大、buffer pool污染、页利用率下降;改用uuid_to_bin(uuid(), 1)可将时间戳高位前置实现近似递增。

为什么mysql的自增主键不建议使用uuid_防止b+树索引频繁分裂与页偏移

为什么UUID主键会触发频繁页分裂

因为InnoDB的聚簇索引按主键值物理排序存储数据,而UUID()生成的是随机128位字符串(如'550e8400-e29b-41d4-a716-446655440000'),每次插入都得在B+树中二分查找插入位置——大概率落在已有页中间,而非末尾。一旦目标页已满,就必须分裂:原页一半数据搬出、新建页、更新父节点指针。这个过程不是“慢一点”,而是直接放大I/O、污染Buffer Pool、降低页利用率。

常见现象包括:SHOW TABLE STATUS里Data_free持续增长;innodb_buffer_pool_reads飙升;写入吞吐随时间明显下滑。

  • 自增ID插入:总追加到B+树叶节点末尾,页分裂极少
  • UUID插入:实测每插入1000行,平均触发1.7次页分裂
  • 页内碎片化后,16KB页原本可存约100条自增记录,UUID可能只剩30–40条

CHAR(36) vs BINARY(16):存储格式没解决根本问题

把id CHAR(36)改成id BINARY(16)能省20字节,但无法改变“随机性”这个核心缺陷。B+树比较的是字节序列,而标准UUID的16字节原始顺序仍是时间戳+随机段混排,高位不单调,插入位置依然不可预测。

例如两个UUID:1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d和6d5c5a5e-5c5a-4e5a-8d5a-5a5e5c5a5e5c,首字节0x1a vs 0x6d就决定排序位置,后续插入完全无局部性。

MySQL
MySQL

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

下载
  • UUID_TO_BIN(UUID())(无swap_flag)仍是随机序,仅节省空间
  • 必须用UUID_TO_BIN(UUID(), 1)才把时间戳高位前置,实现近似递增
  • MySQL swap_flag=1,硬套该写法无效

ORDER BY id变全表扫描?是的,而且很真实

当主键是UUID时,SELECT * FROM t ORDER BY id LIMIT 10几乎必然退化为全表扫描。原因不是SQL写错了,而是物理存储乱序:InnoDB必须遍历所有叶子页才能凑够10条“逻辑上最小”的记录,无法利用B+树的有序链表特性做跳跃式遍历。

对比自增ID:同一查询只需访问1–2个连续页,磁盘I/O次数差一个数量级。

  • 非聚簇索引的叶子节点存的是主键值,UUID越长,单页能存的索引项越少,B+树层级越高
  • 8字节BIGINT主键下,二级索引页通常能存300+条引用;36字节CHAR下可能只剩80条左右
  • 树高每+1,范围查询或多条件JOIN的I/O成本就翻倍

真要分布式唯一ID?别硬扛UUID主键

如果业务确实需要全局唯一且不依赖中心发号器,优先考虑更适配InnoDB的方案,而不是强行用UUID顶主键。

  • UUID_SHORT():8字节整型、天然有序、单机安全,适合管理后台/日志表;注意服务器重启会重置计数器
  • 时间前缀+自增序列:CONCAT(DATE_FORMAT(NOW(), '%y%m%d%H%i%s'), LPAD(@cnt:=@cnt+1, 4, '0')),需配合应用层并发控制
  • MySQL 8.0+ 推荐建表语句:id BINARY(16) PRIMARY KEY DEFAULT (UUID_TO_BIN(UUID(), 1))
  • 已有UUID表想止损?OPTIMIZE TABLE可临时清理碎片,但锁表严重;ALTER TABLE t ENGINE=InnoDB ALGORITHM=INPLACE只整理页内空洞,不重排主键顺序

最常被忽略的一点:页分裂不是一次性损耗,而是随写入持续恶化。上线半年后性能掉30%,往往就始于第一行INSERT INTO t (id) VALUES (UUID())。

相关文章

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

4443

8

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

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

2023.10.27

931

4

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

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

2024.02.23

1169

5

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

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

2024.03.06

6401

10

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

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

2024.03.06

3203

4

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

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

2024.04.07

6360

11

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

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

2024.04.29

8701

6

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

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

2024.04.29

1170

5

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

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

2024.04.29

1032

5

热门下载

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

精品课程

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

共1课时 | 189人学习