针对黑名单的随机唯一字符串

时间:2017-04-02 08:42:34

标签: random unique uuid blacklist

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

  1. 不允许使用某些字符,即I,l和1是相似的,O和0也是如此
  2. 我的初步实施是可靠的并且解决了任务但表现不佳。而且我的意思是说它注定每天都会变得越来越慢。

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

    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多分钟。

    一个真实的用例是在数据库中拥有超过100万个条目,并尝试创建2万个新的唯一代码。

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

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

    最佳, 尼古拉

2 个答案:

答案 0 :(得分:0)

两个选项:

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

    s = "This is a string that uniquely identifies voucher #1.  Blah blah."
    h = hash(s)
    guid = truncate(hash)
    
  2. 从递增计数器生成五个数字,随机生成三个数字。根据你的角色设置,一个小偷在猜测代码的机会中会有不到1分的差异。

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

答案 1 :(得分:0)

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

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