不好意思,UUID 该换了!

简介: 不好意思,UUID 该换了!

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

UUID:Universally Unique Identifier(通用唯一标识符)

为什么不选择UUID

UUID 目前有 5 个版本:


版本1:在许多环境中是不切实际的,因为它需要访问唯一的,稳定的MAC地址,容易被攻击;


版本2:将版本 1 的时间戳前四位换为 POSIX 的 UID 或 GID,问题同上;


版本3:基于 MD5 哈希算法生成,生成随机分布的ID需要唯一的种子,这可能导致许多数据结构碎片化;


版本4:基于随机数或伪随机数生成,除了随机性外没有提供其他信息;


版本5:通过 SHA-1 哈希算法生成,生成随机分布的ID需要唯一的种子,这可能导致许多数据结构碎片化;


这里面常用的就是 UUID4 了,但是,即使是随机的,但是也是存在冲突的风险。


和 UUID 要么基于随机数,要么基于时间戳不同,ULID 是既基于时间戳又基于随机数,时间戳精确到毫秒,毫秒内有1.21e + 24个随机数,不存在冲突的风险,而且转换成字符串比 UUID 更加友好。


推荐一个开源免费的 Spring Boot 最全教程:


https://github.com/javastacks/spring-boot-best-practice


ULID特性:

ulid() # 01ARZ3NDEKTSV4RRFFQ69G5FAV

  • 与UUID的128位兼容性
  • 每毫秒1.21e + 24个唯一ULID
  • 按字典顺序(也就是字母顺序)排序!
  • 规范地编码为26个字符串,而不是UUID的36个字符
  • 使用Crockford的base32获得更好的效率和可读性(每个字符5位)
  • 不区分大小写
  • 没有特殊字符(URL安全)
  • 单调排序顺序(正确检测并处理相同的毫秒)

ULID规范

以下是在python(ulid-py)中实现的ULID的当前规范。二进制格式已实现


01AN4Z07BY      79KA1307SR9X4MV3


|----------|    |----------------|

Timestamp          Randomness

 10chars            16chars

  48bits             80bits



组成

时间戳

  • 48位整数
  • UNIX时间(以毫秒为单位)
  • 直到公元10889年,空间都不会耗尽。

随机性

  • 80位随机数
  • 如果可能的话,采用加密技术保证随机性

排序

最左边的字符必须排在最前面,最右边的字符必须排在最后(词汇顺序)。必须使用默认的ASCII字符集。在同一毫秒内,不能保证排序顺序


编码方式

如图所示,使用了Crockford的Base32。该字母表不包括字母I,L,O和U,以避免混淆和滥用。


0123456789ABCDEFGHJKMNPQRSTVWXYZ

二进制布局和字节顺序

组件被编码为16个八位位组。每个组件都以最高有效字节在前(网络字节顺序)进行编码。


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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      32_bit_uint_time_high                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     16_bit_uint_time_low      |       16_bit_uint_random      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+



应用场景

  • 替换数据库自增id,无需DB参与主键生成
  • 分布式环境下,替换UUID,全局唯一且毫秒精度有序
  • 比如要按日期对数据库进行分区分表,可以使用ULID中嵌入的时间戳来选择正确的分区分表
  • 如果毫秒精度是可以接受的(毫秒内无序),可以按照ULID排序,而不是单独的created_at字段

用法(python)

安装


pip install ulid-py

创建一个全新的ULID。


时间戳记值(48位)来自 time.time(),精度为毫秒。


随机值(80位)来自 os.urandom()。


>>> import ulid

>>> ulid.new()



根据现有的128位值(例如UUID)创建新的ULID 。 支持ULID值类型有 int,bytes,str,和UUID。


>>> import ulid, uuid
>>> value = uuid.uuid4()
>>> value
UUID('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
>>> ulid.from_uuid(value)
<ULID('09GF8A5ZRN9P1RYDVXV52VBAHS')>



从现有时间戳值(例如datetime对象)创建新的ULID 。 支持时间戳值类型有int,float,str,bytes,bytearray,memoryview,datetime,Timestamp,和ULID


>>> import datetime, ulid

>>> ulid.from_timestamp(datetime.datetime(1999, 1, 1))



根据现有的随机数创建一个新的ULID。


支持随机值类型有int,float,str,bytes,bytearray,memoryview,Randomness,和ULID。


>>> import os, ulid

>>> randomness = os.urandom(10)

>>> ulid.from_randomness(randomness)

>>>



一旦有了ULID对象,就有多种与之交互的方法。


timestamp()方法将为您提供ULID的前48位的时间戳快照,而randomness()方法将为您提供后80位的随机数快照。


>>> import ulid
>>> u = ulid.new()
>>> u
<ULID('01BJQM7SC7D5VVTG3J68ABFQ3N')>
>>> u.timestamp()
<Timestamp('01BJQM7SC7')>
>>> u.randomness()
<Randomness('D5VVTG3J68ABFQ3N')>


github:https://github.com/ahawker/ulid



相关文章
|
4月前
|
SQL 存储 Oracle
老程序员分享:Oracle易忘知识点记录
老程序员分享:Oracle易忘知识点记录
|
3月前
|
存储 前端开发 数据可视化
老码农花3分钟给老爸做了个生日祝福的APP,他是怎么做到的呢?
老码农花3分钟给老爸做了个生日祝福的APP,他是怎么做到的呢?
24 1
|
小程序 Java
头铁面试官:一个小小的 System.out.println 硬是考了我半个小时,被问懵了。。
头铁面试官:一个小小的 System.out.println 硬是考了我半个小时,被问懵了。。
100 0
|
存储 安全 JavaScript
金九银十,收下这份 Java String 面试题
金九银十,收下这份 Java String 面试题
132 0
金九银十,收下这份 Java String 面试题
|
机器学习/深度学习 算法 编译器
明天省赛,我都还不太熟悉Dev - C++,怎么切换成C++11了?它的报错看不懂呀,那花八分钟看看这篇文章吧~解决你的困惑。
明天省赛,我都还不太熟悉Dev - C++,怎么切换成C++11了?它的报错看不懂呀,那花八分钟看看这篇文章吧~解决你的困惑。
973 0
明天省赛,我都还不太熟悉Dev - C++,怎么切换成C++11了?它的报错看不懂呀,那花八分钟看看这篇文章吧~解决你的困惑。
【CCCC】L2-029 特立独行的幸福 (25分),模拟题,set用法
【CCCC】L2-029 特立独行的幸福 (25分),模拟题,set用法
168 0
|
存储 负载均衡 算法
HASH碰撞问题一直没真正搞懂?这下不用慌了
散列函数(英语:Hash function)又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums,或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。
HASH碰撞问题一直没真正搞懂?这下不用慌了
|
Java
面经 - 美团面试官问我一个字符的String.length()是多少,我说是1,面试官说你回去好好学一下吧~
面经 - 美团面试官问我一个字符的String.length()是多少,我说是1,面试官说你回去好好学一下吧~
260 0
面经 - 美团面试官问我一个字符的String.length()是多少,我说是1,面试官说你回去好好学一下吧~
|
数据安全/隐私保护 Python
Python编程:shortuuid生成uuid
Python编程:shortuuid生成uuid
221 0
|
存储 缓存 Java
通过这个文章重新再深入认识认识String吧!!文末附有三个小小面试题,一起来试一试吧
通过这个文章重新再深入认识认识String吧!!文末附有三个小小面试题,一起来试一试吧
111 0
通过这个文章重新再深入认识认识String吧!!文末附有三个小小面试题,一起来试一试吧