目录

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的学习及使用相关推荐

  1. ULID 与 UUID:用于 JavaScript 的可排序随机 ID 生成器

    UUID 是软件开发中最常用的通用标识符之一.然而,在过去的几年里,新的替代品挑战了它的存在. 其中,ULID 是领先的竞争对手之一,因为它提供可排序的唯一 ID. 在本文中,我将通过示例讨论 ULI ...

  2. java as uuid_java UUID 源码学习

    UUID 我们平时在使用 UUID 的时候觉得非常简单,甚至很多人觉得这没什么技术含量. 那么深入思考一层,UUID 的实现原理是什么? 源码 类声明 public final class UUID ...

  3. ULID - 一种比UUID更好的方案

    ULID:Universally Unique Lexicographically Sortable Identifier(通用唯一词典分类标识符) UUID:Universally Unique I ...

  4. java测试示例-生成ULID

    ULID全称Universally Unique Lexicographically Sortable Identifier,直译就是通用唯一按字典排序的标识符,原始仓库是https://github ...

  5. Mysql学习总结(81)——为什么MySQL不推荐使用uuid或者雪花id作为主键?

    前言 在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一,单机递增),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么 ...

  6. 初学习数据库,记录1,在sql server数据表主键中插入UUID

    芜湖,看见很多人都在CSDN写文章,记录自己的学习历程,既可以当做笔记,也可以给其它同学一个参考,我觉着挺好的,今天正好遇到一个问题,想着可以记录下来! 如何在sqlserver中生成UUID?看了好 ...

  7. Python 模块 UUID 学习使用

    Python 模块 UUID 学习使用 一.UUID介绍 UUID: 通用唯一标识符 ( Universally Unique Identifier ), 对于所有的UUID它可以保证在空间和时间上的 ...

  8. Python uuid 学习总结

    1. 背景知识: UUID: 通用唯一标识符 ( Universally Unique Identifier ), 对于所有的UUID它可以保证在空间和时间上的唯一性. 它是通过MAC地址, 时间戳, ...

  9. 蓝牙学习笔记(八)——BLE 4.0 的128Bits/32Bist/16Bits的UUID

    前言:对于BLE中的GATT Profile经常用到的UUID(universally unique identifier)做一些概述. 1. 128bits的UUID   UUID作为通用唯一标识符 ...

最新文章

  1. PIC单片机入门_PICC头文件介绍
  2. 港媒:AI技术有望助力中国核潜艇升级
  3. linux usb filesystem
  4. Linux下Java环境变量配置
  5. java自动的废料收集_Java 垃圾收集机制
  6. java se是不是java_Java SE和java EE究竟有什么实质上的区别
  7. 字符串匹配——C++使用Regex
  8. 直方图均衡化原理及c++代码
  9. [C++] STL标准模板库
  10. tshark/wireshark/tcpdump实战笔记(更新中...)
  11. C语言入门——取余运算
  12. 财务自由之路读书笔记
  13. win10用什么清理垃圾好?
  14. 转:Android实时获取音量(单位:分贝)
  15. word文档的生成以及echarts图片的插入
  16. Missing Tag Identification in COTS RFID Systems: Bridging the Gap between Theory and Practice 理解+笔记
  17. 阿里云视频云推出低代码音视频工厂vPaaS
  18. 【总结】本地springboot连接腾讯云Redis
  19. 美国主机网站建设中的安全性和隐私保护措施
  20. 推荐一个免费在线制作gif图片的网站。

热门文章

  1. [滑模控制器浅述] (5) 基于分层滑模的吊车控制
  2. 网络监控系统安装的六种传输方式
  3. 咨询行业细分——管理咨询、战略咨询、IT咨询
  4. JDK8的Stream操作你还不会用吗?
  5. c语言指针 —— 面试题
  6. uni-app实现扫描二维码功能
  7. SQLite数据库database is locked解决
  8. Java中的Switch用法
  9. 至多删三个字符 (35分)
  10. CG cosh, exp, sinh, smoothstep, tanh, perlin_easeCurve1/2 曲线