浅谈postgre-sql uuid生成办法的细节
前言
最近在工作中编写业务sql的时候,忽然对于gen_random_uuid() 这个办法比拟好奇,他在高并发的状况下是否领有强一致性的特点(就是保障主键唯一性),趁着感兴趣钻研了一波,发现有不少有意思的货色能够探讨,所以出了这篇文章来聊聊。
前提条件
我预计很多读者基本不晓得postgreSql是啥玩意,集体起初接触这个数据库也很顺当,并且这个名字很难记,所以业内人士个别叫读这块数据库为:post-gres-s-q-l,集体比拟习惯叫做 pg-sql,对于这一款数据集体认为几个比较突出的特点:
- 齐全开源,基于社区保护,并且社区较为沉闷。
- NoSQL :JSON,JSONB,XML,HStore原生反对,NoSQL数据库的内部数据包装器
- 自从mysql被甲骨文收买之后,mysql的原作者也有参加其中,能够看到不少mysql的影子,比方OLTP和MVCC的实现。
然而遗憾的是尽管postgresql看起来全面强于mysql然而不如mysql风行,并且mysql看上去是有很多令人诟病的历史遗留问题,然而仍然不可否认他仍然是当初的支流数据库。好了对于pg-sql这款数据库就唠叨到这里,明天的主题不是介绍这个数据库,所以咱们来看下重点对于postgre-sql生成uuid的办法。
文章目标
对于本文,咱们将会探讨上面几个话题:
Gen_random_uuid()
怎么来的?
PostgreSQL 13: 新增内置函数
Gen_random_uuid()
生成UUID数据,换句话说这个版本之前须要用手动的装置模式
uuid_generate_v4()
有没有可能反复?
答案是必定的,哪怕是sql原始的gen_randowm_uuid办法也是存在反复的可能性的,然而存在某些“非凡条件”,上面来一起探讨一下起因。
- 比照gen_randowm_uuid()函数和uuid_generate_v4函数的实现差别。
差别次要是生成随机数的形式上,其余工作基本一致。
1. Gen_random_uuid()
怎么来的?
如果postgre-sql的版本应用的是13之前,会抛出上面的问题:
function gen_random_uuid() does not exist
如果想要可能应用此办法,须要应用如下的命令,也就是应用 pgcrypto :
CREATE EXTENSION pgcrypto;
上面是postgresql-sql 12版本,会呈现如下的提醒。
# SELECT gen_random_uuid();
ERROR: function gen_random_uuid() does not exist
LINE 1: select gen_random_uuid();
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
# CREATE EXTENSION pgcrypto;
CREATE EXTENSION
# SELECT gen_random_uuid();
gen_random_uuid
--------------------------------------
19a12b49-a57a-4f1e-8e66-152be08e6165
(1 row)
然而须要留神的是13版本增加的并不是这个库,而是依赖于OSSP库的uuid函数。其实就是gen_random_uuid_v4()
这个函数,上面咱们来看下这个函数的介绍。
1.1 gen_random_uuid_v4()官网介绍
函数有什么作用不用多说,上面是对于官网的介绍:
We create a uuid_t object just once per session and re-use it for all
operations in this module. OSSP UUID caches the system MAC address and
other state in this object. Reusing the object has a number of benefits:
saving the cycles needed to fetch the system MAC address over and over,
reducing the amount of entropy we draw from /dev/urandom, and providing a
positive guarantee that successive generated V1-style UUIDs don't collide.
(On a machine fast enough to generate multiple UUIDs per microsecond,
or whatever the system's wall-clock resolution is, we'd otherwise risk
collisions whenever random initialization of the uuid_t's clock sequence
value chanced to produce duplicates.)
如果看不懂,上面是谷歌翻译之后的介绍:
咱们每个会话只创立一个 uuid_t 对象,并为所有人从新应用它本模块中的操作。 OSSP UUID 缓存零碎 MAC 地址和此对象中的其余状态。
重用对象有很多益处:
1. 节俭一遍又一遍地获取零碎 MAC 地址所需的周期,
2. 缩小咱们从 /dev/urandom 中提取的熵量,并提供一个踊跃保障间断生成的 V1 格调的 UUID 不会发生冲突。
(在足够快的机器上每微秒生成多个 UUID,或者无论零碎的时钟分辨率是多少,否则咱们会冒险
每当随机初始化 uuid_t 的时钟序列时发生冲突值机会产生反复。)
翻译之后发现仍然比拟拗口,集体其实也看不懂,所以这里又找了一篇文章,上面简略阐明一下这篇文章的内容和含意。
参考文章:Is Postgres’s uuid_generate_v4 securely random?
问题:这位老哥的大抵问题就是他应用了postgresql v4版本的uuid() 来生成一个access token的密钥令牌,并且询问是否线程平安(uuid是否惟一),以及是否须要应用利用端保障唯一性,这正好合乎这篇文章的主题。
上面是剖析之后的集体总结进去的答案(每个人理解能力不同,不肯定完全正确):
- 首先,uuid_generate_v4 依赖uuid-ossp这个库,并且13版本的postgres的uuid是依赖此实现的。
- OSSP(版本1.6.2)源代码表明,该代码在类Unix零碎(Windows上的CryptGenRandom())上应用/dev/urandom ,以及基于以后工夫、过程ID的可靠性较差的PRNG,和 C 库 rand() 函数,然而对于这三个后果应用了异或操作,能够极大防止反复,应用 /dev/urandom 足以保障强随机性 。
- 然而如果/dev/urandom因为某些起因失败(例如,该过程过后已用完可用文件描述符),则库将回退到仅应用弱 PRNG ,而不会收回正告 ,这就很恐怖了,这样随机性和可能性大大提高,如果此时呈现并发应用同一个时钟节点。
总结来说就是,基于下面三个点,尽管uuid-ossp在通常状况下能够保障强唯一性,然而存在进化为弱唯一性的可能性,甚至最坏的状况是应用机器的时钟点来生成uuid造成反复uuid,所以这位答主最终的倡议是:审慎倡议不要依赖 PostgreSQL 生成的 UUID 的强随机性,而是在应用程序端明确应用强随机源 。
有读者会问PRNG是啥?伪随机数生成器 (pseudo random number generator,PRNG ),又被称为确定性随机比特生成器 (deterministic random bit generator,DRBG ),[[1]](https://zh.wikipedia.org/wiki/伪随机数生成器#cite_note-1)是一个生成数字序列的算法,其个性近似于随机数序列的个性。PRNG生成的序列并不是真随机,因而它齐全由一个初始值决定,这个初始值被称为PRNG的随机种子(seed,但这个种子可能蕴含真随机数)。只管靠近于真随机的序列能够通过硬件随机数生成器生成,但伪随机数生成器因为其生成速度和可再现的劣势,在实践中也很重要。[[2]](https://zh.wikipedia.org/wiki/伪随机数生成器#cite_note-2)。
话外题:其实很多的策略游戏就是用了伪随机数的算法。
1.2 小结
通过下面的铺垫,咱们大抵理解了uuid_generate_v4() 的根本实现,上面咱们来进行一个简略的总结。
产生状况:
- 如果机器在同一个微秒产生多个uuid就会呈现反复的状况,并且/dev/urandom 生效的时候。
- 没有定义HAVE_UUID_OSSP,则须要调用操作系统的uuid_generate_time或uuid_generate_random来产生UUID。
- 绝大多数状况下如果仅仅只是须要一个随机数的生成函数,官网更加倡议应用pgcrypto的gen_random_uuid()。
2. 比照gen_random_uuid()
和 uuid_generate_v4()
没错,这个也是参考文章的,并且比照了很多材料发现上面这个答案简洁明了:
参考文章:PostgreSQL 生成 UUID 的两种不同形式:gen_random_uuid 与 uuid_generate_v4s
首先是对于这两个函数的间接区别:
gen_random_uuid()
扩大提供pgcrypto
uuid_generate_v4()
扩大提供uuid-ossp
从这篇参考文章得出的基本论断就是:
uuid_generate_v4()
应用 arc4random 来确定随机局部。gen_random_uuid()
应用fortuna代替实现。
对于这两个算法区别这里就不再进行开展了,有条件的能够看一下上面的维基的链接介绍,如果拜访不了也能够自行上网查阅材料,都是一些比拟理论化的货色,这里就不再持续深刻查究了。
参考文章:https://en.wikipedia.org/wiki/RC4#RC4-based_random_number_generators
总结
用这篇文章其实是想通知各位,看待数据库的uuid生成办法须要结合实际的业务是否须要保障uuid的强唯一性,如果须要则强烈建议不要依赖数据库的实现形式,特地是在并发量非常高的状况下,是非常不牢靠的。
最初如果发现有任何谬误欢送斧正。
扩大浏览
1. 如何创立一个适宜PostgreSQL中会话ID的随机字符串?
- https://www.itranslater.com/qa/details/2325807468670092288
发表回复