【问题标题】:Random unique string against a blacklist针对黑名单的随机唯一字符串
【发布时间】:2017-04-02 08:42:33
【问题描述】:

我想创建一个固定长度的随机字符串(在我的用例中为 8 个字符),并且生成的字符串必须区分大小写并且在黑名单中是唯一的。我知道这听起来像一个 UUID,但我有一个特定要求阻止我使用它们

  1. 不允许使用某些字符,即 I、l 和 1 相似,O 和 0 也是如此

我最初的实现是可靠的并且解决了任务,但表现不佳。我所说的糟糕是指它注定每天都会越来越慢。

这是我要优化的当前实现:

private function uuid()
{
    $chars = 'ABCDEFGHJKLMNPQRSTVUWXYZabcdefghijkmnopqrstvuwxyz23456789';

    $uuid = null;
    while (true) {
        $uuid = substr(str_shuffle($chars), 0, 8);

        if (null === DB::table('codes')->select('id')->whereRaw('BINARY uuid = ?', [$uuid])->first())) {
            break;
        }
    }

    return $uuid;
}

请不要批评我,我们生活在一个敏捷的世界中,这个实现是功能性的并且可以快速编码。

只需少量数据,它就可以很好地工作。但是,如果我在黑名单中有 1000 万个条目并尝试再创建 1000 个条目,它会失败,因为它需要 30 多分钟。

一个真正的用例是在数据库中有 10+ 百万个条目并尝试创建 20,000 个新的唯一代码。

我正在考虑预先播种所有允许的值,但这太疯狂了: (24+24+8)^8 = 9.6717312e+13

如果社区可以为我指明正确的方向,那就太好了。

最好, 尼古拉

【问题讨论】:

  • 它是否需要不可预测/不可猜测?您不能简单地使用递增计数器的原因是什么?
  • 不幸的是,它必须是不可预测的。想想优惠券代码。

标签: random unique uuid blacklist


【解决方案1】:

两种选择:

  1. 只需使用独特的哈希值,然后截断,使其适合您标识符的带宽。哈希有时会发生冲突,因此如果代码已在使用中,您仍需要检查数据库并重试。

    s = "This is a string that uniquely identifies voucher #1.  Blah blah."
    h = hash(s)
    guid = truncate(hash)
    
  2. 从递增计数器生成五个数字,随机生成三个。根据您的字符集,小偷猜到密码的几率会低于 140,000 分之一。

    u = Db.GetIncrementingCounter()
    p = Random.GetCharacters(3)
    guid = u + p
    

【讨论】:

  • 你知道输出区分大小写的哈希吗?我想不出任何在我头上的东西。但是,您的回复让我想到了两个想法:1)将 RDBS 表从 uuid 的一列拆分为 3 列或更多列。目标是通过使用更细化的 WHERE 子句来加速数据库检查。 2) 尝试使用 NoSQL 存储和检查黑名单。我会在今天晚些时候尝试它们。
【解决方案2】:

我最终修改了方法:而不是在每个循环上检查 uuid 是否存在,例如50K DB 检查,我现在将生成的代码分成 1000 个代码的多个块,并在事务中发出 INSERT IGNORE 批处理查询。

如果受影响的行与项目一样多(在本例中为 1000 个),我知道没有冲突,我可以提交事务。否则我需要回滚该块并生成另外 1000 个代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 2010-10-18
    • 2014-10-20
    • 1970-01-01
    相关资源
    最近更新 更多