【发布时间】:2018-06-09 10:01:15
【问题描述】:
几个月前,我们使用 UUID 来生成需要全面唯一的随机字符串 ID。然后我更改了算法,以便在我们的数据库中保存一些数据和索引空间。我测试了几种生成唯一字符串 ID 的方法,我决定使用这个函数:
function generateToken($length) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
$max = strlen($characters) - 1;
$token = '';
for ($i = 0; $i < $length; $i++) {
$token .= $characters[mt_rand(0, $max)];
}
return $token;
}
我正在使用此函数生成使用数字和字母的 20 个字符长的 ID,或者您可以说这些 ID 是以 36 为底的数字。任何 2 个 ID 冲突的概率应该是 1/36^20,但是由于生日悖论,可以预计在大约 36^10 条记录之后发生碰撞 - 即 3.6 万亿条记录。然而,就在几个小时前发生了冲突,当时数据库中只有 530 万条现有记录。我是不是很倒霉,还是我的 ID 生成功能在随机性方面存在缺陷?我知道 mt_rand() 并不是真正随机的,但它足够随机,不是吗?
我会编写一个循环来检查生成的 ID 是否唯一,如果不是,则生成一个新 ID,但我认为发生冲突的机会非常小,以至于这样的循环的性能成本不值得。我现在将在代码中包含这样一个循环,但如果确实存在缺陷,我仍然有兴趣完善 ID 生成功能。
【问题讨论】:
-
大多数数据库管理系统都具有生成 uuid 的功能,这些 uuid 保证对于特定数据库实例是唯一的。为什么不使用这些?
-
由于空间限制,我将 UUID 换成了这种基于 36 个 ID 的 ID。我需要将尽可能多的信息打包到一点数据库空间中,同时仍然使用一种生成足够长且足够复杂的 ID 的算法,以使冲突极不可能发生。 UUID 以 16 为基数,包含破折号和一些非随机字符,因此它们的空间效率不如我所愿。
-
你描述的只是一个uuid的字符串表示。 uuid 实际上是一个大小正好为 16 字节的二进制结构。大多数 DBMS 专门支持 uuid 列类型,然后存储 16 字节二进制表示,而不是字符串表示。您使用什么 DBMS?
-
我只是读了一点 mt_rand 基数,我很难相信这是基于随机数生成器的。你是如何产生种子的?可能会发生碰撞吗?例如,将时间作为种子并并行执行程序多次?
-
MySQL 不直接支持 uuid 列类型,但您可以将 uuid 存储在 varbyte(16) 列中,并使用内置函数 uuid_to_bin / bin_to_uuid 在字符串和二进制表示之间进行转换。跨度>
标签: random token uuid birthday-paradox