postgresql数据库自动生成主键通常有如下几种方式:
- sequence序列
- serial和bigserial伪类型
- identity columns
- uuids
- before insert触发器
这些主键方式使用了两个基本的方法:序列和uuid算法。
这两种基本方法在其它数据库使用非常广泛,uuid更是在软件开发中作为最常用的通用标识符。
最近在github看到一个足可与uuid竞争的对手,它就是nano id。
下面是nano id的m6米乐安卓版下载官网介绍:
https://github.com/ai/nanoid/blob/main/readme.zh-cn.md
它是一个小巧、安全、url友好、唯一的javascript字符串id生成器。
nano id的特点
- 小巧. 130 bytes (已压缩和 gzipped), 没有依赖, size limit 控制大小。
- 快速. 它比uuid快两倍。
- 安全. 它使用硬件随机生成器,可在集群中使用。
- 紧凑. 它使用比uuid(a-za-z0-9_-)更大的字母表。 因此,id大小从36个符号减少到21个符号。
- 易用. nano id 已被移植到20种编程语言,包括c#、c 、clojure and clojurescript、coldfusion/cfml、crystal、dart & flutter、deno、go、elixir、haskell、janet、java、nim、ocaml、perl、php、python with dictionaries、ruby、rust、swift、unison、v
与uuid的比较
nano id与uuid v4 (基于随机) 相当。 它们在 id 中有相似数量的随机位 (nano id 为126,uuid 为122),因此它们的冲突概率相似:
要想有十亿分之一的重复机会, 必须产生103万亿个版本4的id.
nano id 和 uuid v4之间有三个主要区别:
- nano id使用更大的字母表,所以类似数量的随机位被包装在21个符号中,而不是36个。
- nano id代码比uuid/v4 包少 4倍: 130字节而不是483字节。
- 由于内存分配的技巧,nano id 比 uuid 快两倍。
下面这个网址显示这两者之间npg的趋势
https://www.npmtrends.com/nanoid-vs-uuid
下面的统计数据可以看到nano id星星数量(16k)超过了uuid(12k)。
基准测试
$ node ./test/benchmark.js
crypto.randomuuid 25,603,857 ops/sec
@napi-rs/uuid 9,973,819 ops/sec
uid/secure 8,234,798 ops/sec
@lukeed/uuid 7,464,706 ops/sec
nanoid 5,616,592 ops/sec
customalphabet 3,115,207 ops/sec
uuid v4 1,535,753 ops/sec
secure-random-string 388,226 ops/sec
uid-safe.sync 363,489 ops/sec
cuid 187,343 ops/sec
shortid 45,758 ops/sec
async:
nanoid/async 96,094 ops/sec
async customalphabet 97,184 ops/sec
async secure-random-string 92,794 ops/sec
uid-safe 90,684 ops/sec
non-secure:
uid 67,376,692 ops/sec
nanoid/non-secure 2,849,639 ops/sec
rndm 2,674,806 ops/sec
测试配置: thinkpad x1 carbon gen 9, fedora 34, node.js 16.10.
安全性
请看一篇关于随机生成器理论的好文章:
-
不可预测性. 不使用不安全的 math.random(), nano id使用node.js的crypto模块和浏览器的web crypto api。 这些模块使用不可预测的硬件随机生成器。
-
统一性. random % alphabet 是编写id生成器时常犯的一个错误。 符号的分布是不均匀的; 有些符号出现的几率会比其他符号低。因此, 它将减少刷新时的尝试次数。 nano id 使用了一种更好的算法,并进行了一致性测试。
-
有据可查: nano id所有的行为都有记录。参考中的注释。
-
漏洞: 报告安全漏洞,请与安全联系人协调修复和披露。
postgresql如何支持nano id
github上提供了nano id的postgresql实现:
先创建pgcrypto扩展
create extension if not exists pgcrypto;
再创建nanoid函数
create or replace function public.nanoid(size integer default 21)
returns text
language plpgsql
stable
as $function$
declare
id text := '';
i int := 0;
urlalphabet char(64) := '_-0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz';
bytes bytea := gen_random_bytes(size);
byte int;
pos int;
begin
while i < size loop
byte := get_byte(bytes, i);
pos := (byte & 63) 1;
id := id || substr(urlalphabet, pos, 1);
i = i 1;
end loop;
return id;
end
$function$;
使用nanoid函数,我们可以控制想要的长度,比如使用nanoid(2)更快,生成更简单的id:
postgres=# select nanoid(2);
nanoid
--------
sz
(1 row)
默认长度是21:
postgres=# select nanoid();
nanoid
-----------------------
yrlohp53juqe-r68faok3
(1 row)
接着我们在postgresql 14.2里对nano id和uuid进行简单的插入性能对比测试,表结构如下:
create unlogged table test_uuid (
id uuid default gen_random_uuid() primary key
);
create unlogged table test_nanoid (
id character varying default nanoid() primary key
);
我用自己的虚拟机(内存1g,cpu 1核)使用单条insert批量插入一万条数据。
select 'insert into test_uuid values('||repeat('default),(',99999)||'default);';\gexec
或者
select 'insert into test_nanoid values('||repeat('default),(',99999)||'default);';\gexec
测试结果:uuid花销大约700毫秒,nano id花销大约8秒。
因为在postgresql里uuid类型可以很好的映射byte,而nano id必须存储在text/varchar,并且postgresql从v13开始原生支持uuid生成函数,受这两个因素的影响,目前uuid的性能比nano id要好。
不过相比uuid,从上面nano id的特性介绍来看,nano id有很大的优势,也许在postgresq里很快能看到对nano id原生或者插件的支持。
参考文章
https://github.com/ai/nanoid
https://www.npmtrends.com/nanoid-vs-uuid
https://blog.bitsrc.io/why-is-nanoid-replacing-uuid-1b5100e62ed2
https://dev.to/bibekkakati/nanoid-alternative-to-uuid-2kgn
https://www.libhunt.com/compare-nanoid-vs-pg_random_id
https://github.com/jakeii/nanoid-postgres
保持联系
现组建了一个pg乐知乐享交流群,欢迎关注文章的小伙伴加微信进群吹牛唠嗑,交流技术。