
小福利: ULID---另一种半有序高效全局ID出现了
1、前言:
关于全局唯一ID,高效的节省空间的ID,似乎是一个很有意思的话题。曾经在PostgreSQL中的UUID的弊端及缓解和解决方案这一文中介绍了UUID的弊端以及NanoID(省空间,快速,但无序)还有有序的UUID。当然最近还有UUID-v7的新版本。
如果综合来看,可能不如现下的ULID来得更直接。
1.1 UUID
通常,UUID的标准形式由32个十六进制数字组成,以连字符分为8-4-4-4-12五段的格式。
第一段(8个十六进制数字) :时间戳的低位部分。
第二段(4个十六进制数字) :时间戳的中间部分。
第三段(4个十六进制数字) :
前两位表示UUID版本(目前共有5种版本)。
后两位通常是时间戳的高位部分。
第四段(4个十六进制数字) :
前两位用于表示“时钟序列”的高位部分,这是为了处理时钟回拨的问题。
后两位是时钟序列的低位部分。
第五段(12个十六进制数字) :这部分通常是基于机器的
MAC地址或随机生成。
1.2 ULID
ULID(Universally Unique Lexicographically Sortable Identifier)是一种全局唯一的标识符,其由Alizain Feerasta于2016年提出。通俗来讲,ULID基于UUID(Universally Unique Identifier)和时间戳的形式,并采用了一种特殊的字母数字编码的方式,其可以将时间戳作为排序的依据。
通常ULID由26个字符组成,并使用Crockford的Base32进行编码。上述生成的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' ||n from generate_series(1, 500000) as 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
