小福利: ULID---另一种半有序高效全局ID出现了


小福利: ULID---另一种半有序高效全局ID出现了

1、前言:

关于全局唯一ID,高效的节省空间的ID,似乎是一个很有意思的话题。曾经在PostgreSQL中的UUID的弊端及缓解和解决方案这一文中介绍了UUID的弊端以及NanoID(省空间,快速,但无序)还有有序的UUID。当然最近还有UUID-v7的新版本。

如果综合来看,可能不如现下的ULID来得更直接。

1.1 UUID

通常,UUID的标准形式由32个十六进制数字组成,以连字符分为8-4-4-4-12五段的格式。

  1. 第一段(8个十六进制数字) :时间戳的低位部分。

  2. 第二段(4个十六进制数字) :时间戳的中间部分。

  3. 第三段(4个十六进制数字)

  • 前两位表示UUID版本(目前共有5种版本)。

  • 后两位通常是时间戳的高位部分。

  1. 第四段(4个十六进制数字)

  • 前两位用于表示“时钟序列”的高位部分,这是为了处理时钟回拨的问题。

  • 后两位是时钟序列的低位部分。

  1. 第五段(12个十六进制数字) :这部分通常是基于机器的MAC地址或随机生成。

1.2 ULID

ULID(Universally Unique Lexicographically Sortable Identifier)是一种全局唯一的标识符,其由Alizain Feerasta2016年提出。通俗来讲,ULID基于UUID(Universally Unique Identifier)和时间戳的形式,并采用了一种特殊的字母数字编码的方式,其可以将时间戳作为排序的依据。

通常ULID26个字符组成,并使用CrockfordBase32进行编码。上述生成的ULID中各组成部分含义如下:

  • 前10个字符 01AN4Z07BY 代表时间戳,这部分是基于毫秒级Unix时间(从1970年1月1日以来的毫秒数)编码的。这种时间戳表示使得ULID在生成时具有可排序性。

  • 后16个字符 79KA1307SR9X4MV3 是随机生成的或伪随机生成的,用于确保全局唯一性。并且人性化的去掉了几个容易混淆的字符。

2、分析验证

我们简单理解成一个特征就是,前10个字符是描述时间序的,基本有序。后16个字符伪随机。就这一点,可以让PG中的BTree能尽量保持均衡,而不会那么膨胀还低效。存储空间也比UUID少了一大片。

因为是使用PostgreSQL数据库,我们就不重点介绍算法本身,直接从https://github.com/andrielfn/pg-ulid 这个扩展入手。

git clone https://github.com/andrielfn/pg-ulid
[07:29:11-postgres@centos1:/iihero/source/plugins/pg-ulid]$ cd pg-ulid && make && sudo su -c "PATH=/usr/pgsql-14/bin:$PATH make install"

postgres=# create extension ulid;
CREATE EXTENSION

-- 建样表
CREATE TABLE test_ulid (
  id ulid NOT NULL DEFAULT gen_ulid(
) PRIMARY KEY,
  name text NOT NULL
)
;

postgres=# \timing on
Timing is on.
postgres=# insert into test_ulid (name) select 'test' ||n from generate_series(1, 500000) as n;
INSERT 0 500000
Time: 14351.296 ms (00:14.351)

postgres=# select * from test_ulid limit 5;
             id             | name  
----------------------------+-------
 01HJPSYM0100042J1415GHJNJJ | test1
 01HJPSYM03000AM6E1080ZGX9C | test2
 01HJPSYM030003N4C7NCYYF6A3 | test3
 01HJPSYM030005H73GB1J4A9BS | test4
 01HJPSYM03000EWRQEQZ843CH1 | test5
(5 rows)

CREATE TABLE test_uuid (
  id VARCHAR(36
) PRIMARY KEY,
  name text NOT NULL
)
;

insert into test_uuid select ''||gen_random_uuid(), 'test' ||from generate_series(1500000as n;
INSERT 0 500000
Time: 19783.420 ms (00:19.783)

时间上ULID略快。再看索引大小:
postgres=# select pg_indexes_size('test_ulid'), pg_indexes_size('test_uuid');
 pg_indexes_size | pg_indexes_size 
-----------------+-----------------
        15843328 |        38273024
(1 row)

--两倍以上的差距。

小结:

ULID,用于新系统 ,绝对值。性能、空间、易用性。网上大家自己也能找到它的各种语言的实现版本。开发应用起来,也非常简单。

参考:

0、https://github.com/andrielfn/pg-ulid 【pg-ulid】

1、Sequential UUID Generators by https://www.2ndquadrant.com
https://www.2ndquadrant.com/en/blog/sequential-uuid-generators

2、jNanoid
https://github.com/aventrix/jnanoid

3、pg_nanoid:  https://github.com/jaiminpan/pg_nanoid

4、https://juejin.cn/post/7309301549155287075


请使用浏览器的分享功能分享到微信等