首页 > 编程语言 >Go 语言 UUID 库 google/uuid 源码解析:UUID version7 的实现

Go 语言 UUID 库 google/uuid 源码解析:UUID version7 的实现

时间:2024-07-17 18:58:38浏览次数:19  
标签:google UUID +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ UU

google/uuid 库地址

建议阅读内容

在阅读此篇文章之前,建议先了解 UUIDv1 的构成、UUIDv4 的 API 以及掌握位运算。

了解 UUIDv1 的构成可以参考Go 语言 UUID 库 google/uuid 源码解析:UUID version1 的实现RFC 9562

了解 UUIDv4 的 API 可以看Go 语言 UUID 库 google/uuid 源码解析:UUID version4 的实现

位运算可以参考详解位运算(&、|、、&、>>、<<)

相较于 UUIDv1,UUIDv7 的改进

UUIDv7 是 UUIDv1 的优化版本,其优化有三点:

  1. 使用自 1970 年 1 月 1 日午夜(Unix 纪元时间戳源)以来的毫秒数代替自 1582 年 10 月 15 日以来的 100 纳秒数作为时间戳。

  2. UUIDv7 在序列中保持时间戳的顺序(UUIDv1 会对时间戳进行重排),这意味着生成的 UUID 会按时间顺序排列。优化在数据库中作为索引时的性能表现。

  3. 随机生成序列中的 74 位(UUID 总共 128 位),增加熵特性,减少逆向推导的可能性(UUIDv1 包含 MAC 地址)。

UUIDv7 的结构介绍

UUIDv7 主要由三部分组成(以下陈述并没有按顺序排列):

  1. 在最高的 48 位分配的Unix时间戳。

  2. 6 位标志位(2 位变体标识,4位版本标识)。

  3. 以及随机填充的74位。

UUIDv7 具体的字段和位具体布局如下:

(表格顶部的两行数字用于表示位数,00,01,…,10,11,…,20,21,…,30,31)

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           unix_ts_ms                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          unix_ts_ms           |  ver  |  rand_a (12 bit seq)  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                        rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

实现

UUID 存储在 type uuid 中

type uuid [16]byte

uuid[0:5]: 时间戳
uuid[6] 高 4 位:版本号
uuid[6] 低 4 位 与 uuid[7]: 随机值part1
uuid[8:15]:随机值part2

具有 v7 特色的时间戳的制作

var lastV7time int64

const nanoPerMilli = 1000000

// getV7Time 返回毫秒和纳秒 / 256 的时间。
// 返回的 (milli << 12 + seq) 保证大于
// 任何之前对 getV7Time 的调用返回的 (milli << 12 + seq)。
func getV7Time() (milli, seq int64) {
	timeMu.Lock()
	defer timeMu.Unlock()

	nano := timeNow().UnixNano()
	milli = nano / nanoPerMilli
	// 序列号在 0 到 3906 之间(nanoPerMilli>>8)
	seq = (nano - milli*nanoPerMilli) >> 8
	now := milli<<12 + seq
	if now <= lastV7time {
		now = lastV7time + 1
		milli = now >> 12
		seq = now & 0xfff
	}
	lastV7time = now
	return milli, seq
}

函数 getV7Time 实际上生成两部分内容:milli 和 seq。

milli 就是时间戳,而 seq 是随机值part1。

milli 就是通过 time.Now().UnixNano() 获取的 Unix 纪元到当前时间的纳秒数 / 1x10^6 次方得到的。(因为1毫秒 = 1x10^6纳秒)。需要注意的是,/ 会导致纳秒部分精度丢失。

seq 则等于 (nano - milli * nanoPerMilli) >> 8,因为 milli 在进行 / 时导致纳秒精度丢失,所以 nano - milli * nanoPerMilli 的结果就是丢失的纳秒数,>>8 等于除以 256。因为丢失的纳秒数徘徊在 0~999999 之间,所以 seq 的值在 0 ~ 3906 之间。

同时,我们还会记录上次生成的时间信息(milli << 12 + seq),通过比较上次的时间信息和当前的时间信息,判断其是否保持递增,如果没有递增则在上次的时间信息基础上再重新计算 milli 和 seq。

序列生成第一步:填充随机值

UUIDv7 的生成是从向 uuid 的所有位中填充随机值开始的,然后再将对应位置变成正确的内容。而填充随机值的方式便是使用 UUIDv4 的 API:NewRandom 或者 NewRandomFromReader,简单说就是将 uuid(16bytes) 的 128 位随机填满。

注:UUIDv4 生成的时候也会填充版本号和变体号,因为 UUIDv7 版本号和 UUIDv4 不同,所以会覆盖版本号,但是变体号并不会被覆盖,所以后续不再填充变体号。

UUIDv4 具体的字段和位具体布局如下:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           random_a                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          random_a             |  ver  |       random_b        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                       random_c                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           random_c                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

后续过程会在 random_a 的位置填充 milli,在 random_b 的位置填充 seq,而 var 和 random_c 的位置已经是最终值。

序列生成第二步:填充时间戳与版本号

// makeV7 填充 48 位时间(uuid[0] - uuid[5]),设置版本 b0111(uuid[6])
func makeV7(uuid []byte) {
	_ = uuid[15] // 边界检查

	t, s := getV7Time()

	uuid[0] = byte(t >> 40)
	uuid[1] = byte(t >> 32)
	uuid[2] = byte(t >> 24)
	uuid[3] = byte(t >> 16)
	uuid[4] = byte(t >> 8)
	uuid[5] = byte(t)

	uuid[6] = 0x70 | (0x0F & byte(s>>8))
	uuid[7] = byte(s)
}

理解这段代码,需要知道 UUIDv7 的时间戳部分只有 48 位,所以会从 t 中截取 48 位放置到 UUID 的高 48 位中。byte() 的用途是截取其低 8 位进行填充,>> 操作的目的是将高位移到低位,如 uuid[0] = byte(t >> 40) 就是将 t 右移 40 位,然后截取当前低 8 位放置到 uuid[0] 中。

0x70 | (0x0F & byte(s>>8)) 的作用是在 uuid[6] 中同时设置版本号(高 4 位为 0111)和随机值part1的一部分(低 4 位)。

序列生成第三步:整合调用

func NewV7() (UUID, error) {
	uuid, err := NewRandom()
	if err != nil {
		return uuid, err
	}
	makeV7(uuid[:])
	return uuid, nil
}

整合步骤如下:

  1. 调用 NewRandom 填充随机值,使用 uuid 接收返回的 UUIDv4
  2. 检查错误
  3. 调用 makeV7 填充时间戳和版本号
  4. 返回 UUIDv7

完整函数调用关系图

在这里插入图片描述

到这里一个完整的 UUIDv7 便完成了。

以上就是 UUIDv7 实现的所有内容,希望你能有所收获。

参考资料

RFC 9562
uuid package

标签:google,UUID,+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+,UU
From: https://blog.csdn.net/a2025834646/article/details/140441251

相关文章

  • cerebro 报错: Oops, cannot start the server. com.google.common.util.concurrent.Un
    @目录前言环境异常修改方案第二次报错修改方式成功前言使用elasticsearch+springboot实现新闻搜索功能:https://javapub.net.cn/star/project/news-search-es/遇到的错误记录。环境cerebro下载地址:https://github.com/lmenezes/cerebro使用了最新的版本:cerebro-0......
  • 基于java+springboot+vue实现的实验室管理系统(文末源码+Lw)127
    基于SpringBoot+Vue的实现的实验室管理系统(源码+数据库+万字Lun文+流程图+ER图+结构图+演示视频+软件包)系统功能:实验室管理系统管理员功能有个人中心,学生管理,教师管理,公告信息管理,知识库管理,实验课程管理,实验室信息管理,实验室预约管理,实验设备管理,采购记录管理,维修记录管理......
  • 基于java+springboot+vue实现的汽车资讯网站(文末源码+Lw)125
    基于SpringBoot+Vue的实现的汽车资讯网站系统(源码+数据库+万字Lun文+流程图+ER图+结构图+演示视频+软件包)系统功能:汽车资讯网站有管理员和用户。管理员功能有个人中心,用户管理,汽车品牌管理,价格分类管理,经销商管理,汽车信息管理,留言板管理,系统管理等。用户可以查看各种汽车信......
  • 基于java+springboot+vue实现的中药实验管理系统(文末源码+Lw)124
    基于SpringBoot+Vue的实现的中药实验管理系统(源码+数据库+万字Lun文+流程图+ER图+结构图+开题报告+演示视频+软件包)系统功能:本中药实验管理系统有管理员,教师,学生,实验员。管理员功能有个人中心,学生管理,教师管理,实验员管理,实验教学管理,在线学习管理,实验信息管理,实验预约管理,实......
  • 基于java+springboot+vue实现的学生选课系统(文末源码+Lw)129
     系统角色:学生、教师、管理员系统功能:管理员管理专业,教师,学生,课程,查看学生选课以及学生成绩信息。教师查询学生,查询课程,审核学生选课信息,为学生学习的课程进行成绩打分学生参与课程选课,查询选课信息,查询已选课程的成绩信息。技术栈:开发语言:JavaJDK版本:JDK8前端......
  • 基于java+springboot+vue实现的中小企业人事管理系统(文末源码+Lw)128
     基于SpringBoot+Vue的实现的中小企业人事管理系统(源码+数据库+万字Lun文+流程图+ER图+结构图+ppt+演示视频+软件包)系统角色:员工、管理员系统功能:管理员登录进入中小企业人事管理系统可以查看首页、个人中心、员工管理、部门信息管理、职位信息管理、福利信息管理、培......
  • 基于java+springboot+vue实现的实验室管理系统(文末源码+Lw)127
     基于SpringBoot+Vue的实现的实验室管理系统(源码+数据库+万字Lun文+流程图+ER图+结构图+演示视频+软件包)系统功能:实验室管理系统管理员功能有个人中心,学生管理,教师管理,公告信息管理,知识库管理,实验课程管理,实验室信息管理,实验室预约管理,实验设备管理,采购记录管理,维修记录......
  • Nacos(二)源码分析Nacos服务端注册示例流程
    上回我们讲解了客户端配置好nacos后,是如何进行注册到服务器的,那我们今天来讲解一下服务器端接收到注册实例请求后会做怎么样的处理。首先还是把博主画的源码分析图例发一下,让大家对整个流程有一个大概的理解:图示流程地址:https://www.processon.com/view/link/5f7e895......
  • SpringBoot Security OAuth2实现单点登录SSO(附源码)
    文章目录基础概念1.用户认证2.单点登录(SSO)3.授权管理4.安全性和配置逻辑实现配置认证服务器配置SpringSecurity两个客户端页面展示本篇小结更多相关内容可查看附源码地址:https://gitee.com/its-a-little-bad/SSO.git基础概念SpringBootSecurity和OAut......
  • 基于Java+SpringBoot+Vue的学生评奖评优管理系统的设计与开发(源码+lw+部署文档+讲解
    文章目录前言项目背景介绍技术栈后端框架SpringBoot前端框架Vue数据库MySQL(MyStructuredQueryLanguage)具体实现截图详细视频演示系统测试系统测试目的系统功能测试系统测试结论代码参考数据库参考源码获取前言......