Mysql中页分裂、合并的问题
一、UUID作为主键
如果使用UUID作为主键,因为UUID是随机字符串,那么它在有序的B+tree是如何排序的。
1、UUID在B+Tree中的存储方式
UUID的字节序比较
// UUID内部表示为16字节的二进制数据
// 比较规则:从左到右按字节比较// 示例UUID比较:
UUID1: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
UUID2: 6d5c5a5e-5c5a-4e5a-8d5a-5a5e5c5a5e5c// 比较过程:
// 第一个字节:0x1a vs 0x6d → UUID1 < UUID2
// 因为 0x1a = 26, 0x6d = 109注意: UUID 视为连续的 16 个字节, UUID1 的字节序
列(十六进制):1a 2b 3c 4d 5e 6f 7a 8b 9c 0d 1e 2f 3a 4b 5c 6d
0x1a中前面0x表示16进制。其实就基本上可以理解每个字符对比,从左到右依次对比。
如果是长度不同的,短的排在前面
2、UUID作为主键的性能问题
2.1 插入性能问题
比如B+tree中已经存在一个字符是B的节点,因为UUID生成是随机的可能生成了一个A,
可能生成了一个C,那么A就会插入到B的前面,前面的树都排好的,A一插入就会导致树结构
需要重排。但是自增ID或者顺序插入,不会影响B+tree的问题。
2.2 B+Tree页分裂问题
1、顺序写入
-- 假设B+Tree的叶子节点容量为3条记录
-- 当前数据页状态:
页1: [1, 2, 3] -- 已满
页2: [4, 5, 6] -- 已满
页3: [7, 8] -- 有空间-- 插入新记录:id = 9
-- 过程:直接追加到页3 → [7, 8, 9] ✅
-- 无页分裂,性能极佳
2、UUID的插入过程(随机写入)
-- 同样的B+Tree,使用UUID主键
-- 当前数据页状态(按UUID排序):
页1: [uuid_a, uuid_c, uuid_f] -- 已满
页2: [uuid_h, uuid_m, uuid_p] -- 已满
页3: [uuid_r, uuid_z] -- 有空间-- 插入新记录:uuid_k(随机生成)
-- 需要找到插入位置:在uuid_h和uuid_m之间
-- 但页2已满!触发页分裂过程
二、使用UUID解决排序问题:
1、自定义有序UUID生成
// 前8字节:当前时间戳(毫秒)+ 后8字节:随机数,确保同一毫秒内的唯一性
long timestamp = System.currentTimeMillis();
bb.putLong(timestamp);
2、优先使用自增ID 或 雪花算法(有序字符串)
3、MySQL 8.0的UUID_TO_BIN
小结
总结:不用UUID, 就算是无需的字符串作为主键,或者字符串作为非主键索引,都会有同样的问题,所以不是特殊场景,尽量不要使用字符串作索引或者保持字符串是有序的插入。
所以在主键选择上(插入性能+防止页分裂):自增ID > 雪花算法(有序且分布式唯一)> 有序UUID(平衡唯一性和性能) > UUID(随机)
三、Mysql删除数据对页的影响
1、删除中间数据时的B+Tree变化(可能会导致页合并)
-- 初始状态:B+Tree叶子节点
页1: [1, 3, 5] -- 已用空间 100%
页2: [7, 9, 11] -- 已用空间 100%
页3: [13, 15] -- 已用空间 66%-- 删除中间数据:删除记录 7
页2: [9, 11] -- 已用空间 66%,有剩余空间-- 删除不会导致分裂,反而可能触发合并!(当页的使用率低于50%时,触发页合并)
2、InnoDB的实际对页合并做了优化机制
1、删除操作的惰性处理
-- InnoDB不会立即进行页合并,而是:
-- 1. 标记记录为已删除(逻辑删除)
-- 2. 在后台由purge线程清理
-- 3. 在合适时机进行页合并-- 查看删除状态
SHOW ENGINE INNODB STATUS;
-- 在输出中查找 "PHYSICAL DELETES" 和 "LOGICAL DELETES"
所以我们的业务表,最好是定义一个逻辑删除字段:is_Deleted
