使用 SecureRandom.hex 时获得重复的机会有多大?

What are the chances of getting a duplicate when using SecureRandom.hex?

我打算使用 SecureRandom.hex 为我的用户生成 API 密钥。

到目前为止,这里是 3 次执行的输出:

Loading development environment (Rails 5.2.1)
2.3.5 :001 > SecureRandom.hex
 => "0369e9b7c6ffa07bd8d0a263f7b4cfa6" 
2.3.5 :002 > SecureRandom.hex
 => "1a8a168d7f70676451e3d59353e22693" 
2.3.5 :003 > SecureRandom.hex
 => "94cc188e9e5c3abfe587510fa79993ce"

我得到重复结果的几率有多大?

我创建的这个方法真的可以避免产生重复的内容吗?

def generate_string
  string = SecureRandom.hex

  generate_string if Model.where(:key => string).count > 0

  string
end

unique_string = generate_string

我正在使用递归,如果字符串已经存储在数据库中,它只会产生另一个。

而且,由于我没有得到重复项,我可以用 SecureRandom.hex 生成多少个字符串才能生成组合?

由于它是以 16 为基数的数字系统,因此有 16 ** SecureRandom.hex.length 种可能的变化,数量相当多。

如果您不想在用户数量增加时溢出堆栈,最好使用带有退出条件的循环。

MAX_ATTEMPTS = 3 # For you to choose.

def key
  MAX_ATTEMPTS.times do
    hex = SecureRandom.hex
    return hex unless Model.where(key: hex).exists?
  end

  fail 'No attempts to generate a key left.'
end

在您的示例中(当使用默认长度为 32 的 SecureRandom.hex 时)有

16**32 = 340282366920938463463374607431768211456

可能有不同的十六进制值。这意味着 1:340282366920938463463374607431768211456 有机会创建一个副本。这个机会极低,恕我直言,太担心它没有多大意义。

当您打算将该键存储在数据库中时,我建议向该数据库列添加一个唯一索引,以确保在数据库级别上不可能存储任何重复项。

此外,您询问您的示例代码是否足以避免重复。答案是。由于概率低,这是高度理论化的,但是您可能 运行 进入竞争条件,在这种情况下,两个作业同时生成相同的密钥,同时检查数据库中是否没有这样的密钥,并且两者都作业将相同的值存储到 table.

tl;dr 重复的可能性极低。只有数据库中 key 列的唯一索引才能确保永远不会有任何重复项(因为竞争条件或绕过此方法生成的密钥)。

在您的设置中,键的长度似乎是 32。

  • 三次执行不重复的几率是:

    ((16**32 - 1)/16**32) * ((16**32 - 2)/16**32)
    

    所以在三次执行中某处获得(至少一对)重复的机会是:

    1 - ((16**32 - 1)/16**32) * ((16**32 - 2)/16**32)
    = 8.8162076e-39
    
  • 有:

    16**32 = 3.4028237e+38
    

    不同的字符串。

  • 你的方法在按预期工作时可以避免重复,但它也有难以想象的渺茫机会陷入永久和永不终止。