UUIDv1秒变v6的位运算魔法:uuid-creator TimeOrderedCodec源码剖析
【免费下载链接】uuid-creatorUUID Creator is a Java library for generating Universally Unique Identifiers. It generates all standard identifiers from UUIDv1 to UUIDv7. It also provides an alternative to the classic JDK's UUID (alt.GUID), many codecs (codec.*) and several utilities (util.*).项目地址: https://gitcode.com/gh_mirrors/uu/uuid-creator
uuid-creator 是一个开源 Java 库,可生成 UUIDv1 到 UUIDv7 全部标准标识符。本文带你剖析它的 TimeOrderedCodec 源码——仅凭几行位运算,就能把老式 UUIDv1 秒变时间可排序的 UUIDv6,无需任何字符串拼接或第三方依赖。
为什么要把 UUIDv1 转成 UUIDv6?
先搞清楚痛点。UUIDv1 的时间戳被"打散"存放在三个互不相邻的字段里(time_low、time_mid、time_high),导致按字符串排序时,先后生成的 ID 在字典序上是乱序的。
这对数据库很不友好:
- B+ 树写入碎片化:主键随机散落,页分裂频繁,写入性能差
- 范围查询低效:想按时间取"最近 100 条",索引帮不上忙
- 缓存命中率低:新记录无法追加在索引末尾
而 UUIDv6 的魔法在于:把 60 位时间戳重排成一个连续的大端整数,字符串字典序 ≈ 时间先后序。这正是 TimeOrderedCodec 要完成的唯一任务。
核心原理:v1 与 v6 的位布局对比
UUID 的 128 位分为高 64 位(msb)和低 64 位(lsb)。TimeOrderedCodec 只动 msb,lsb(时钟序列 + 48 位节点 ID)原样保留:
| 位段(msb 64位) | UUIDv1 布局 | UUIDv6 布局 |
|---|---|---|
| bit 63~48 | time_high(12位) | time_high(12位) |
| bit 47~32 | time_mid(16位) | time_mid(16位) |
| bit 31~16 | time_low(16位) | time_mid 续… version + time_low 高位 |
| bit 15~12 | version = 0001 | version = 0110 |
| bit 11~0 | time_low 低12位… time_high 高位 | time_low 低12位 |
一句话总结转换逻辑:
v1 → v6:把打散的时间戳重新拼成连续整数,版本号从 1 改成 6。v6 → v1:原路拆散回三个字段,版本号改回 1。
源码剖析:3 个十六进制常数就是全部魔法
源码文件位于src/main/java/com/github/f4b6a3/uuid/codec/other/TimeOrderedCodec.java,整个类不到 100 行。
正向转换:v1 秒变 v6
encode方法的核心只有 4 行(约第 62~70 行):
long timestamp = UuidUtil.getTimestamp(uuid); long msb = ((timestamp & 0x0ffffffffffff000L) << 4) // | (timestamp & 0x0000000000000fffL) // | 0x0000000000006000L; // set version 6 long lsb = uuid.getLeastSignificantBits(); return new UUID(msb, lsb);逐行拆解这 3 个"魔数":
timestamp & 0x0ffffffffffff000L—— 取 60 位时间戳的高 52 位(砍掉最低 12 位)<< 4—— 整体左移 4 位,把版本号的 4 个"坑位"腾出来| (timestamp & 0xfff)—— 把刚才砍掉的低 12 位补到最末尾| 0x6000—— 在 bit 15~12 写入0110,宣告"我是 UUIDv6"
妙处在于:左移 + 低位补回,等于把 60 位时间戳无损地"折叠"成一个大端排列——高位在前、低位在后,字典序排序即时间排序。全程没有任何循环和字符串操作,纯位运算,纳秒级完成。
注意时间戳不是直接读出来的,而是委托给src/main/java/com/github/f4b6a3/uuid/util/UuidUtil.java中的getTimestamp()(约第 272 行),它会按 UUID 版本选择正确的拆位策略。
反向转换:v6 还原 v1
decode方法(约第 81~102 行)就是把"折叠"的动作原样展开:
long timeHigh = (timestamp & 0x0fff_0000_00000000L) >>> 48; // 拆出 12 位高位 long timeMid = (timestamp & 0x0000_ffff_00000000L) >>> 16; // 拆出 16 位中段 long timeLow = (timestamp & 0x0000_0000_ffffffffL) << 32; // 拆出 32 位低位 long version = 0x0000000000001000L; // Set version 1每个"掩码 + 移位"组合,把 v6 连续时间戳中对应的字段搬回 v1 的原始位置,最后或上0x1000把版本号改回 1。由于 encode/decode 是严格互逆的,往返转换零信息损失。
动手实践:一行代码完成转换
// v1 → v6 UUID uuid1 = UuidCreator.getTimeBased(); UUID uuid6 = new TimeOrderedCodec().encode(uuid1); // v6 → v1(可无限往返) UUID back = new TimeOrderedCodec().decode(uuid6);类中还提供了单例TimeOrderedCodec.INSTANCE,生产环境推荐复用它避免重复 new。如果不想手动转换,也可以直接用门面 APIUuidCreator.getTimeOrdered()一步生成 v6(底层工厂在src/main/java/com/github/f4b6a3/uuid/factory/standard/TimeOrderedFactory.java)。
测试怎么保证正确?
配套的src/test/java/com/github/f4b6a3/uuid/codec/other/TimeOrderedCodecTest.java用 100 次循环做了三重断言:
| 测试点 | 验证目标 |
|---|---|
isTimeOrdered(uuid6) | 产物确实是合法的 v6 |
| 时间戳相等 | 转换不丢时间精度 |
decode(encode(v1)) == v1 | 往返转换完全可逆 |
总结
- TimeOrderedCodec 用3 个掩码 + 移位 + 或运算完成了 v1 与 v6 的互转,是位运算在 ID 设计中的教科书级应用
- 核心收益:ID 天然按时间有序,显著提升 B+ 树数据库的写入与范围查询性能
- 源码仅 ~100 行且依赖极简,非常适合通读学习——建议结合
UuidUtil的getTimestamp一起看
掌握这 4 行核心代码后,你甚至可以在任何语言里复现这套"时间有序化"技巧。
【免费下载链接】uuid-creatorUUID Creator is a Java library for generating Universally Unique Identifiers. It generates all standard identifiers from UUIDv1 to UUIDv7. It also provides an alternative to the classic JDK's UUID (alt.GUID), many codecs (codec.*) and several utilities (util.*).项目地址: https://gitcode.com/gh_mirrors/uu/uuid-creator
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考