👉这是一个或许对你有用的社群
🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:
《项目实战(视频)》:从书中学,往事上“练”
《互联网高频面试题》:面朝简历学习,春暖花开
《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题
《精进 Java 学习指南》:系统学习,互联网主流技术栈
《必读 Java 源码专栏》:知其然,知其所以然
👉这是一个或许对你有用的开源项目
国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构
RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRM、AI大模型、IoT物联网等功能:
多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro
微服务:https://gitee.com/zhijiantianya/yudao-cloud
视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本
UUIDv4 的三宗罪
UUIDv7:时间有序的新标准
Java 26 终于原生支持了
单调递增的陷阱
UUIDv7 vs Snowflake vs ULID
迁移指南:从 v4 到 v7
UUID.randomUUID()大概是 Java 里被调用得最多的工具方法之一。从数据库主键到分布式 traceId,到处都有它的身影。用了这么多年,大家也习惯了它"能用就行"的定位。
但 UUIDv4 有一个老问题一直没解决:它是完全随机的,没有时间信息,塞进 B+ Tree 索引里会造成大量页分裂。这个问题在高写入场景下是实打实的性能杀手。
Java 26 终于把这个坑填上了——原生支持 UUIDv7。
UUIDv4 的三宗罪
1. 索引性能差
UUIDv4 是 122 位随机数,新插入的值在已有数据中的位置完全随机。对于 B+ Tree 索引来说,这意味着几乎每次插入都要加载不同的页面——随机写。在 MySQL InnoDB 里,这个问题尤其严重,因为聚簇索引(主键索引)的物理存储顺序就是按主键排的,随机主键 = 随机 IO。
实测数据:1000 万条记录的表,UUIDv4 主键比自增 ID 主键的写入速度慢3-5 倍,索引体积大30-50%。
2. 不可排序
没有时间信息,无法按生成顺序排序。想知道"哪条记录先创建的"?对不起,UUIDv4 帮不了你。很多团队不得不额外加一个created_at字段来弥补。
3. 信息密度低
128 位全给了随机数,除了保证唯一性之外,不携带任何业务信息。同样 128 位的空间,可以做更多事。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
UUIDv7:时间有序的新标准
2024 年 5 月,RFC 9562 正式发布,定义了 UUIDv7 规范。核心变化:
UUIDv7 结构(128 bits): ┌─────────────────────────────────────┐ │ 48-bit Unix Timestamp (毫秒精度) │ ← 前 48 位是时间戳 │ 4-bit Version (0111) │ │ 12-bit Random │ │ 2-bit Variant (10) │ │ 62-bit Random │ ← 剩余 74 位随机数 └─────────────────────────────────────┘关键改进:
时间有序:前 48 位是毫秒级 Unix 时间戳,天然按生成时间排序
索引友好:新记录总是追加在索引末尾,从随机写变成顺序写
保持唯一性:74 位随机数足以保证同一毫秒内不重复
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
Java 26 终于原生支持了
// Java 26 新增 API UUID uuid = UUID.nameUUIDv7(); // 提取时间戳 Instant timestamp = uuid.getTimestamp(); // 一行代码搞定,不再需要第三方库之前想用 UUIDv7,要么引com.fasterxml.uuid(Java UUID Generator),要么自己手撸。Java 26 终于把它变成了标准 API。
注意:UUID.randomUUID()仍然生成 v4,新方法是UUID.nameUUIDv7(),别搞混了。
单调递增的陷阱
UUIDv7 是"时间有序",但同一毫秒内生成的多个 UUID 并不保证严格递增——毫秒内的顺序由随机数部分决定。
在高并发场景下(比如同一毫秒内生成上百个 UUID),这可能导致局部乱序。如果你的场景对严格递增有要求(比如用作消息队列的 offset),需要在应用层加一个单调递增的计数器:
// 单调递增的 UUIDv7 生成器(伪代码) publicclass MonotonicUUIDv7 { privatelong lastTimestamp = 0; privatelong counter = 0; public synchronized UUID next() { long now = System.currentTimeMillis(); if (now == lastTimestamp) { counter++; } else { lastTimestamp = now; counter = 0; } // 用 counter 替换随机数的高位部分 return buildUUIDv7(now, counter); } }大多数场景不需要这么做。数据库主键、分布式 ID 这些常见用途,UUIDv7 默认的"毫秒级有序 + 毫秒内随机"已经足够好。
UUIDv7 vs Snowflake vs ULID
对比维度 | UUIDv7 | Snowflake | ULID |
|---|---|---|---|
长度 | 128 bit (36 字符) | 64 bit (19 位数字) | 128 bit (26 字符) |
时间精度 | 毫秒 | 毫秒 | 毫秒 |
有序性 | 毫秒级有序 | 严格递增 | 毫秒级有序 |
标准化 | RFC 9562 | 无(各家实现不同) | 社区规范 |
依赖 | 无 | 需要 Worker ID 分配 | 需要第三方库 |
JDK 原生 | Java 26+ | ❌ | ❌ |
适合场景 | 分布式系统通用 ID | 高并发严格递增场景 | 需要紧凑编码的场景 |
选型建议:
默认选 UUIDv7:标准化、JDK 原生、够用
需要严格递增 + 紧凑:Snowflake(但要自己搞 Worker ID 分配)
需要 URL 友好的短 ID:ULID(26 字符 Crockford Base32)
迁移指南:从 v4 到 v7
新项目:直接用UUID.nameUUIDv7(),没有理由再用 v4。
老项目迁移:
// 1. 新记录用 v7 UUID newId = UUID.nameUUIDv7(); // 2. 判断已有 UUID 的版本 UUID existingId = UUID.fromString("..."); if (existingId.version() == 4) { // 旧数据,v4 格式 } else if (existingId.version() == 7) { // 新数据,v7 格式 }v4 和 v7 可以在同一张表里共存——它们都是标准 UUID 格式,只是版本号不同。迁移不需要一步到位,新数据用 v7、老数据保持 v4 就行。
数据库层面:如果主键是CHAR(36)或BINARY(16),不需要改表结构。如果之前建了索引,切换到 v7 后索引效率会自动提升(因为新数据变成了顺序插入)。
欢迎加入我的知识星球,全面提升技术能力。
👉 加入方式,“长按”或“扫描”下方二维码噢:
星球的内容包括:项目实战、面试招聘、源码解析、学习路线。
文章有帮助的话,在看,转发吧。 谢谢支持哟 (*^__^*)