我想创建一个固定长度的随机字符串(在我的用例中为8个字符),生成的字符串必须区分大小写并且对黑名单是唯一的。我知道这听起来像一个UUID,但我有一个特定的要求阻止我使用它们
我的初步实施是可靠的并且解决了任务但表现不佳。而且我的意思是说它注定每天都会变得越来越慢。
这是我想要优化的当前实现:
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
如果社区可以指出我正确的方向,那就太好了。
最佳, 尼古拉
答案 0 :(得分:0)
两个选项:
只需使用一些唯一的哈希值,然后截断,使其符合标识符的带宽。哈希值有时会发生冲突,因此您仍需要检查数据库,并在代码已被使用时重试。
s = "This is a string that uniquely identifies voucher #1. Blah blah."
h = hash(s)
guid = truncate(hash)
从递增计数器生成五个数字,随机生成三个数字。根据你的角色设置,一个小偷在猜测代码的机会中会有不到1分的差异。
u = Db.GetIncrementingCounter()
p = Random.GetCharacters(3)
guid = u + p
答案 1 :(得分:0)
我最终修改了这个方法:而不是在每个循环上检查uuid是否存在,例如50K DB检查,我现在将生成的代码分成多个1000个代码块,并在事务中发出INSERT IGNORE批处理查询。
如果受影响的行与项目一样多(本例中为1000),我知道没有发生冲突,我可以提交事务。否则我需要回滚块并生成另外1000个代码。