要求是在用户单击“提交”按钮时向数据库发送唯一ID。所以我使用的是Javascript Math.random
方法。我只是想知道使用Math.random
得到相同数字和比特大小的机会或可能性是什么。
答案 0 :(得分:11)
你反对生日问题:即使有366个可能的生日,当你在一个房间里只有26个人时,一对生日相同的机会优于50-50 。通常,当您的数字接近样本大小的平方根时,可能会发生碰撞(26位于366的平方根附近)。
Javascript的Math.random()有大约52位的随机性。因此,当你的记录数接近2 ** 26(大约是6千万,这是一个相当适中的数据库大小)时,可能会发生冲突。
您应该使用至少128位,最好是256位的加密安全PRNG,以避免冲突。可能有现成的UUID库。
对于给定数量的键 k 和键空间 N ,碰撞的大概几率为:
1 - exp(( - k *(k-1))/(2 * N))
因此,对于k = 100万条记录,N = 2 ** 52,如果我做了正确的数学计算,则在9000中大约为1。这进一步假设Javascript的Math.random()真正使用了可用的填充52位随机性......这也是我不会做出的假设。
答案 1 :(得分:3)
不要这样做。信任(多个)客户提出独特的价值是行不通的。
即使每个客户端都保证生成唯一值,您也可以让两个客户端生成相同的值。由于大多数伪随机数生成器都以当前时间播种,因此当您获得更多用户时,这种情况就更有可能。
如果您正在创建数据库记录,那么您的数据库应该提供这样的功能。大多数或所有SQL数据库都有自动增量概念,许多NoSQL数据库都有一个等价物(Mongo肯定会用于ID)。允许数据库处理这个问题对性能有好处(它可以设置索引并分配空间来很好地处理ID),并且数据库对数据有最终决定权,因此它可以保证ID是唯一的。
如果失败了,您可以让服务器端生成一些唯一标识符(如UUID)并使用它。让服务器执行此操作并使用已知良好的算法(如类型4 UUID)可确保充分的随机性,从而不会发生冲突。请注意,除非您的数据库具有类型,否则使用UUID将具有与顺序ID非常不同的索引性能。
答案 2 :(得分:0)
如果您承诺使用客户端哈希函数,请使用crypto.getRandomValues()
这是一个示例实现:
const randomHash = function () {
let array = new Uint32Array(1);
let hash = window.crypto.getRandomValues(array);
if (myExistingIds.includes(hash[0])) {
randomHash();
} else {
myExistingIds.push(hash[0]);
}
}
此函数将递归地拒绝生成的哈希,直到找到ID集合中尚未存在的哈希为止,该哈希仅需要进行几次迭代-很有可能只有一次迭代。
答案 3 :(得分:-2)
我更喜欢使用时间戳,客户端或用户相关标识符生成的随机数。
答案 4 :(得分:-2)
在我的浏览器(某些版本的Chrome)中,每个Math.random
调用都会返回一个小数,精度为17小数。
这意味着两个Math.random
值相同的可能性是
((1/10)(1/10))^17
或0.0000000000000000000000000000000001
(34个零)。
但是,这是一个特定于客户端的功能。
如果有人愿意,他们可以将功能更改为每次返回相同的值,或者他们想要的任何内容,因此我不会使用Math.random
将唯一ID传递给数据库。
相反,我建议查看this stackoverflow question,这个UUID generating jsFiddle或创建自己的随机数生成器服务器端并传递JavaScript种子,或保证随机性和安全性的东西。