ULID和UUID|ULID的学习及使用
目录
1 什么是UUID
1.1 UUID的定义
1.2 UUID的组成
1.3 UUID的版本
1.4 UUID存在的问题
2 什么是ULID
2.1 ULID的组成
2.2 ULID的特点
2.3 ULID的应用场景
2.4 ULID的溢出错误处理
2.5 ULID的二进制布局
3 Java使用ULID
3.1 pom文件引入依赖:
3.2 使用Demo
3.2.1 常规生成ULID
3.2.2 生成单调排序ULID
3.2.3 测试单调性ULID大小比较
3.2.4 获取ULID的时间部分,随机数部分,创建时刻
1 什么是UUID
1.1 UUID的定义
UUID(Universally Unique Identifier),翻译为中文是通用唯一识别码,UUID的目的是让分布式系统中的所有元素都能有唯一的识别信息。每个人都可以创建不与其他人冲突的UUID,就不需要考虑数据库创建的时候名称重复的问题;
UUID 是由一组32位数的16进制数字所构成,是故 UUID 理论上的总数为16^32=2^128,约等于3.4 x 10^123。即若每秒产生一百万个UUID,要花100亿年才会将所有的UUID用完。
UUID生成ID的时候,只考虑随机性或者是时间戳,生成一个36个字符的长字符串
1.2 UUID的组成
UUID的十六个八位字节被表示为32个十六进制的数字,以连字号分页的五组来显示,形式为8-4-4-4-12,总共有36个字符【32个英文、数字字母和四个连字号】
例如:
123e4567-e89b-12d3-a456-426655440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
数字 M 的四位表示 UUID 版本,当前规范有5个版本,M可选值为1,2,3,4,5;
数字 N 的一至四个最高有效位表示 UUID 变体( variant ),有固定的两位 10xx 因此只可能取值8, 9, a, b;
1.3 UUID的版本
UUID版本通过M表示,当前规范有5个版本,M可选值为1, 2, 3, 4, 5。这5个版本使用不同算法,利用不同的信息来产生UUID,各版本有各自优势,适用于不同情景。具体使用的信息
version 1, date-time & MAC address【日期时间和mac地址】
version 2, date-time & group/user id【日期时间和小组/用户id】
version 3, MD5 hash & namespace【MD5哈希值和命名空间】
version 4, pseudo-random number【伪随机数】
version 5, SHA-1 hash & namespace【安全散列算法1和命名空间】
SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
使用较多的是版本1和版本4,其中版本1使用当前时间戳和MAC地址信息。版本4使用(伪)随机数信息,128bit中,除去版本确定的4bit和variant(变体)确定的2bit,其它122bit全部由(伪)随机数信息确定。
因为时间戳和随机数的唯一性,版本1和版本4总是生成唯一的标识符。
若希望对给定的一个字符串总是能生成相同的 UUID,使用版本3或版本5。
Java中 UUID 使用版本4进行实现,所以由java.util.UUID类产生的 UUID,128个比特中,有122个比特是随机产生,4个比特标识版本被使用,还有2个标识变体被使用。
1.4 UUID存在的问题
UUID不是128 bit随机编码(由128 bit随机数通过编码生成字符串)的最高效实现方式
UUID的v1/v2实现依赖唯一稳定MAC地址不现实,v3/v4/v5实现因为随机性产生的ID会"碎片化"。
2 什么是ULID
ULID(Universally Unique Lexicographically Sortable Identifier)通用唯一词典分类标识符;ULID生成ID的时候,会同时考虑随机性和时间戳来生成id,并将他们编码为26个字符串(128位)
2.1 ULID的组成
ULID 的前10个字符表示时间戳,后16个字符表示随机性。这两个部分都是base 32编码字符串,分别使用48位和80位表示。
ULID规范的字符串表示形式的长度是确定的,共占据26个字符。
ULID规范的字符串表示形式如下:
ttttttttttrrrrrrrrrrrrrrrrwhere
t is Timestamp (10 characters)【
时间戳】
r is Randomness (16 characters)【随机数】例如:
ULID
01FHZXHK8PTP9FVK99Z66GXQTX
ULID分解如下
时间戳 (48 bits) - 01FHZXHK8P
随机数 (80 bits) - TP9FVK99Z66GXQTX
注意:
ULID的时间戳部分是以UNIX时间(以毫秒为单位)表示,知道公元10889年才会耗尽空间。
ULID 使用 Crockford 的 Base32 字母表 (0123456789ABCDEFGHJKMNPQRSTVWXYZ) 进行编码。它不包括 I、L、O 和 U 字母以避免任何意外的混淆。
2.2 ULID的特点
设计为128 bit大小,与UUID兼容
ULID是既基于时间戳又基于随机数,时间戳精确到毫秒,不存在冲突的风险,每毫秒生成1.21e+24个唯一的ULID(高性能)
按字典顺序(字母顺序)排序
标准编码为26个字符的字符串,而不是像UUID那样需要36个字符
使用Crockford base32算法来提高效率和可读性(每个字符5 bit)
不区分大小写
没有特殊字符串(URL安全,不需要进行二次URL编码)
单调排序(正确地检测并处理相同的毫秒,所谓单调性,就是毫秒数相同的情况下,能够确保新的ULID随机部分的在最低有效位上加1位)
词典可排序熊是ULID最突出的特点之一。最左边的字符必须排在最前面,最右边的字符必须排在最后(词汇顺序)。必须使用默认的ASCII字符集,在统一毫秒之内,不能保证排序顺序;
//单调排序举例:
monotonicUlid() // 01GGS5FGGZA4DPDE82PHAEB7SZ
monotonicUlid() // 01GGS5FGGZA4DPDE82PHAEB7T0
monotonicUlid() // 01GGS5FGGZA4DPDE82PHAEB7T1
monotonicUlid() // 01GGS5FGGZA4DPDE82PHAEB7T2
...
monotonicUlid() // 01GGS5FGGZA4DPDE82PHAEB7TZ
monotonicUlid() // 01GGS5FGGZA4DPDE82PHAEB7T0可以看到上边的时间戳部分都为:01GGS5FGGZ,随机数部分只有最后以为一次增大,这就是
ULId的单调性
2.3 ULID的应用场景
替换数据库自增id,无需DB参与主键生成;
分布式环境下,替换UUID,全局唯一且毫秒精度有序;
如果要按照日期对数据库进行分区分表,可以使用ULID中嵌入的时间戳来选择正确的分区分表
如果毫秒精度内是可以接受的(毫秒内无序),可以按照ULID进行排序,二不是单独的created_at字段;
2.4 ULID的溢出错误处理
从技术实现上来看,26个字符的Base32编码字符串可以包含130 bit信息,而ULID只包含128bit的信息,所以可以使用Base32算法对ULID进行编码。
基于Base32编码算法能有生成的最大的合法的ULID数是:7ZZZZZZZZZZZZZZZZZZZZZZZZZ,并且使用的时间戳最大为纪元时间:281474976710655,即2^48-1。对于任何大于这个值的ULID进行解码或者编码的尝试都应该改被所有实现拒绝,以防止溢出错误。
ULID溢出错误测试:
如果时间戳时间大于最大值,则会出现java.lang.IllegalArgumentException: Invalid time value的错误;
2.5 ULID的二进制布局
0 1 2 30 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_time_high |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 16_bit_uint_time_low | 16_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 32_bit_uint_random |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
二进制布局的多个部分被编码为16 byte,每个部分都以最高字节优先(网络字节序,也就是big-endian)进行编码;
3 Java使用ULID
3.1 pom文件引入依赖:
<!-- https://search.maven.org/artifact/com.github.f4b6a3/ulid-creator -->
<dependency><groupId>com.github.f4b6a3</groupId><artifactId>ulid-creator</artifactId><version>5.1.0</version>
</dependency>
3.2 使用Demo
3.2.1 常规生成ULID
生成普通的ULID的方法:
UlidCreator.getUlid()
UlidCreator.getUlid(final Long time)【参数time – 自 1970-01-01(Unix 纪元)以来的毫秒数】
@Testvoid ulidTest(){Ulid ulid = UlidCreator.getUlid(1670837655000l);Ulid ulid1 = UlidCreator.getUlid(1670837655000l);Ulid ulid2 = UlidCreator.getUlid(1670837655000l);log.info("返回结果:" + ulid + "***********"+ ulid1 + "*********" + ulid2);log.info("ULID时间:"+ String.valueOf(Ulid.getTime(ulid.toString())) +"***********"+ String.valueOf(Ulid.getTime(ulid1.toString())) +"***********"+ String.valueOf(Ulid.getTime(ulid2.toString())));}返回结果:01GM2TYNER7TMDSJCJRARCHP6A***********01GM2TYNER1CKWEFH0HDSR0YQB*********01GM2TYNERC7BH0VZ8G3NPJNCZULID时间:1670837655000***********1670837655000***********1670837655000
结论:
根据返回结果可以得到结果的时间戳部分是相同的,但是他的随机数部分是随机的没有排序顺序没有单调性;
根据已ULID时间可以得到这些ULID的时间是相同的;
3.2.2 生成单调排序ULID
生成单调排序的ULID的方法:
UlidCreator.getMonotonicUlid()
UlidCreator.getMonotonicUlid(final Long time)【参数time – 自 1970-01-01(Unix 纪元)以来的毫秒数】
@Test
void ulidMonotonicTest(){Ulid monotonicUlid = UlidCreator.getMonotonicUlid(1670837655000l);Ulid monotonicUlid1 = UlidCreator.getMonotonicUlid(1670837655000l);Ulid monotonicUlid2 = UlidCreator.getMonotonicUlid(1670837655000l);log.info("结果是:" + monotonicUlid + "***********"+ monotonicUlid1 + "*********" + monotonicUlid2); log.info("ULID时间:"+ String.valueOf(Ulid.getTime(monotonicUlid .toString())) +"***********"+ String.valueOf(Ulid.getTime(monotonicUlid1.toString())) +"***********"+ String.valueOf(Ulid.getTime(monotonicUlid2.toString())));
}返回结果:01GM2TYNERZ5GQDAG9Q7Y56YC3***********01GM2TYNERZ5GQDAG9Q7Y56YC4*********01GM2TYNERZ5GQDAG9Q7Y56YC5ULID时间:1670837655000***********1670837655000***********1670837655000
结论:
根据返回结果可以得到结果的时间戳部分是相同的,它的随机数部分也是相同的,但是随机数部分的最后一位逐次+1,提现了ULID的单调性;
根据已ULID时间可以得到这些ULID的时间是相同的;
3.2.3 测试单调性ULID大小比较
方法:
compareTo();
@Testvoid ulidSortTest(){Ulid monotonicUlid = UlidCreator.getMonotonicUlid(1670837655000l);Ulid monotonicUlid1 = UlidCreator.getMonotonicUlid(1670837655000l);Ulid monotonicUlid2 = UlidCreator.getMonotonicUlid(1670837655000l);log.info("结果是:" + monotonicUlid + "***********"+ monotonicUlid1 + "*********" + monotonicUlid2);log.info(String.valueOf(monotonicUlid.compareTo(monotonicUlid1)));log.info(String.valueOf(monotonicUlid.compareTo(monotonicUlid)));log.info(String.valueOf(monotonicUlid2.compareTo(monotonicUlid)));}结果是:01GM2TYNERJGWQQ9SA4BYQ4Y7C***********01GM2TYNERJGWQQ9SA4BYQ4Y7D*********01GM2TYNERJGWQQ9SA4BYQ4Y7E-1 (小于)0 (等于)1 (大于)
结论:
根据获取到的结果,可以可出他的单调ULID,也可以看出他的随机数部分是在最低位依次增大,之后使用compareTo方法也可以对其进行大小比较;
3.2.4 获取ULID的时间部分,随机数部分,创建时刻
方法:
获取时间:Ulid.getTime(String);
获取随机数:Ulid.getRandom(String);
获取创建时刻:Ulid.getInstant(String);
@Testvoid getTime(){Ulid monotonicUlid = UlidCreator.getMonotonicUlid();log.info(monotonicUlid.toString());String s = monotonicUlid.toString();log.info("时间是:"+String.valueOf(Ulid.getTime(s)));log.info("随机数部分是:"+String.valueOf(Ulid.getRandom(s)));log.info("创建时刻是:"+String.valueOf(Ulid.getInstant(s)));Date date = new Date(Ulid.getInstant(s).toEpochMilli());long time = date.getTime();log.info("日期时间是:"+date.toString());log.info("时间戳是:"+String.valueOf(time));}时间是:1670837655000随机数部分是:[B@7ecec90d创建时刻是:2022-12-12T09:34:15Z日期时间是:Mon Dec 12 17:34:15 CST 2022
结论:
根据代码运行结果可以得出,ULID可以通过方法获取到他的时间戳部分、随机数部分、以及创建ULID的时刻;
如果毫秒精度内是可以接受的(毫秒内无序),可以通过获取ULID的时间戳部分进行排序;
ULID Creatorhttps://github.com/f4b6a3/ulid-creator
关于Base32加密算法https://www.jianshu.com/p/1fce3f5d9201
ULID和UUID|ULID的学习及使用相关推荐
- ULID 与 UUID:用于 JavaScript 的可排序随机 ID 生成器
UUID 是软件开发中最常用的通用标识符之一.然而,在过去的几年里,新的替代品挑战了它的存在. 其中,ULID 是领先的竞争对手之一,因为它提供可排序的唯一 ID. 在本文中,我将通过示例讨论 ULI ...
- java as uuid_java UUID 源码学习
UUID 我们平时在使用 UUID 的时候觉得非常简单,甚至很多人觉得这没什么技术含量. 那么深入思考一层,UUID 的实现原理是什么? 源码 类声明 public final class UUID ...
- ULID - 一种比UUID更好的方案
ULID:Universally Unique Lexicographically Sortable Identifier(通用唯一词典分类标识符) UUID:Universally Unique I ...
- java测试示例-生成ULID
ULID全称Universally Unique Lexicographically Sortable Identifier,直译就是通用唯一按字典排序的标识符,原始仓库是https://github ...
- Mysql学习总结(81)——为什么MySQL不推荐使用uuid或者雪花id作为主键?
前言 在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一,单机递增),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么 ...
- 初学习数据库,记录1,在sql server数据表主键中插入UUID
芜湖,看见很多人都在CSDN写文章,记录自己的学习历程,既可以当做笔记,也可以给其它同学一个参考,我觉着挺好的,今天正好遇到一个问题,想着可以记录下来! 如何在sqlserver中生成UUID?看了好 ...
- Python 模块 UUID 学习使用
Python 模块 UUID 学习使用 一.UUID介绍 UUID: 通用唯一标识符 ( Universally Unique Identifier ), 对于所有的UUID它可以保证在空间和时间上的 ...
- Python uuid 学习总结
1. 背景知识: UUID: 通用唯一标识符 ( Universally Unique Identifier ), 对于所有的UUID它可以保证在空间和时间上的唯一性. 它是通过MAC地址, 时间戳, ...
- 蓝牙学习笔记(八)——BLE 4.0 的128Bits/32Bist/16Bits的UUID
前言:对于BLE中的GATT Profile经常用到的UUID(universally unique identifier)做一些概述. 1. 128bits的UUID UUID作为通用唯一标识符 ...
最新文章
- PIC单片机入门_PICC头文件介绍
- 港媒:AI技术有望助力中国核潜艇升级
- linux usb filesystem
- Linux下Java环境变量配置
- java自动的废料收集_Java 垃圾收集机制
- java se是不是java_Java SE和java EE究竟有什么实质上的区别
- 字符串匹配——C++使用Regex
- 直方图均衡化原理及c++代码
- [C++] STL标准模板库
- tshark/wireshark/tcpdump实战笔记(更新中...)
- C语言入门——取余运算
- 财务自由之路读书笔记
- win10用什么清理垃圾好?
- 转:Android实时获取音量(单位:分贝)
- word文档的生成以及echarts图片的插入
- Missing Tag Identification in COTS RFID Systems: Bridging the Gap between Theory and Practice 理解+笔记
- 阿里云视频云推出低代码音视频工厂vPaaS
- 【总结】本地springboot连接腾讯云Redis
- 美国主机网站建设中的安全性和隐私保护措施
- 推荐一个免费在线制作gif图片的网站。