一、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
到此这篇关于mysql中页分裂、合并的问题的文章就介绍到这了,更多相关mysql页分裂合并内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论