我读了几个类似我要问的主题,但似乎没有一个对我很有帮助。
我有一个表单,用户可以在其中生成存储在具有唯一约束的列中的代码。代码是长度为7个字符的字符串。用户可以输入一个数字,程序会生成许多代码,这可以重复,直到达到最大代码数。
我的问题是重复值。但是在输入新条目时我没有使用已存在于数据库中的值(我成功检查了这些值),但新组(例如10000)代码中的某些条目(可能)是相同的。所以我的代码在同一个事务中生成两个(或更多)相同的代码,并且DB中的Unique约束抱怨它。
我想在每次输入后检查数据库,但考虑到我们谈论的是10000或有时更多的条目,这是非常耗时的。
所以现在我认为唯一的选择是首先修改生成它们的代码,因为它似乎效率低下并生成双精度。
问题的很大一部分是所需的代码长度,否则我会使用纯'uniqid()'或类似的东西,但因为我必须将它限制为7个字符,我想这会让它变得更糟。另外,我必须从代码中[标记为'problem_characters']的代码中排除一些字符。
这是代码,我无法正确修改它以仅生成唯一值。
$problem_characters = array("0", "o", "O", "I", "1", 1);
$code = md5(uniqid(rand(), true));
$extId = strtoupper(str_replace($problem_characters,rand(2,9),substr($code, 0, 7)));
//insert $extId in the database
@Geo 好的,我尝试了你的解决方案并且它正在工作(当然),但后来我遇到了一个新问题 - 在'if'部分你的'if'我正在做以下事情:
$extId = strtoupper(str_replace($problem_characters,rand(2,9),substr($code, 0, 7)));
while(true){
if((!in_array($extId, $allExternalIdsHandled)) && (!in_array($extId, $newEnteredValues))){
break;
}else{
$extId = strtoupper(str_replace($problem_characters,rand(2,9),substr($code, 0, 7))); }
}
//insert the modified value in the DB here
所以,现在它正在进入一个无限循环并且它没有突破'break'命令,即使它应该随着'随机'调用的执行而改变,然后输入if和break out ...... / p>
我没有在这里看到问题。有人可以给我一些方向吗?
编辑:它有时挂起,有时不挂。我刚刚输入了10000个值,并通过'else'路径修改了两个条目。我用日志观察了这个。答案 0 :(得分:3)
已经libraries为您做了艰苦的工作,允许您选择在生成字符串时使用的“字母”以及字符串的长度。
您的“相同条目”问题称为collision,无法避免。
修改因此,类似于Geo建议的内容,我使用PHP
创建n
个唯一条目列表。不同之处在于SQL插入可能会失败,所以我有2层迭代来确保填充所需的总数:
<?php
require('hashids.php'); // I'm using the library I suggested
$hashids = new hashids('some salt', 7); // use the default alphabet, feel free to pass the 3rd parameter with the alphabet you want to use
$generationTries = 0;
$hashesInDBCount = 0; // get from your database
$desiredHashesCount = 50; // use a parameter
$totalDesiredHashes = $hashesInDBCount + $desiredHashesCount;
do
{
// when coming back in the loop, only generate what's still required
$desiredHashesCount = $totalDesiredHashes - $hashesInDBCount;
$generatedHashesCount = 0;
$generatedHashes = array();
while($generatedHashesCount < $desiredHashesCount)
{
$hash = $hashids->encrypt($generationTries++);
if(!in_array($hash, $generatedHashes))
{
array_push($generatedHashes, $hash);
++$generatedHashesCount;
}
}
// insert $generatedHashes in your Database
$hashesInDBCount = 50; // again, query your database as you might come through this loop more than once,
// I'm hardcoding the value to have a working example
}
while($hashesInDBCount < $totalDesiredHashes);
echo "Generated " . count($generatedHashes) . " hashes in " . $generationTries . " tries\n";
var_dump($generatedHashes);
这给了我一个像:
的输出Generated 50 hashes in 50 tries
array(50) {
[0]=>
string(7) "eAcgAcx"
[1]=>
string(7) "Exidai8"
[2]=>
string(7) "ExTbqT8"
[3]=>
string(7) "4Acz8cB"
[4]=>
string(7) "LRipxir"
[5]=>
string(7) "zATe5Tx"
...
}
每次添加随机盐都会为您提供随机值
答案 1 :(得分:1)
<?php
$problem_characters = array('0', 'o', 'O', 'I', '1', 1);
$length = 10000;
$i = 0;
$hashes = array();
while ($i < $length) {
$code = md5(uniqid(rand(), TRUE));
$extId = strtoupper(str_replace($problem_characters, rand(2, 9), substr($code, 0, 7)));
if ( ! in_array($extId, $hashes)) {
$hashes[] = $extId;
$i++;
// insert $extId in the database
}
}
答案 2 :(得分:1)
首先 - 你正在使用md5来生成字符串代码,因为md5是十六进制编码的字符串,你正在严重减少可能的组合数量,生成随机字符串有30个可能的字符给你21 bilion(10 ^ 9)可能性而不是2.68亿(10 ^ 6)与十六进制字符
其他东西 - 你永远不能创造真正独特的价值(guid-s是机器唯一的),两次生成相同值的概率会随着字符串的缩短而增加。
我可以使用三种不同的方法(我假设你至少有30个没有问题的字符) 你可以创造独特的非随机值。让我们说你有两个计数器生成请求计数和请求计数器。因此,如果用户1要求100个代码,那么像user_request_counter-code_counter:'00 -00-00_00-00-00-01'到'00 -00-01_00-00-03-00'这样的代码肯定是唯一的(并且是实际上是7 - 每组两个数字,最多30个可以用单个字符表示(就像十六进制一样有16个字符 - 你可以选择你喜欢的任何基数)这将允许你创建30 ^ 4(810,000)代码到30 ^ 3(27,000)个用户。这样你就不需要使用昂贵的随机调用,也不需要担心重复的代码。你甚至可以通过随机化优惠券计数器并随机分配每个代码生成请求来使它有点随机。比计数器,但每个用户的代码仍然具有相同的前缀
我使用的第二种方法是简单地用随机代码填充数据库,然后将它们分配给用户,这很有用,因为您只需要偶尔执行一次,并且新代码生成可以是完成离线完成(转储数据库),然后推送到服务器,这样你就可以获得令人敬畏的性能o(1)在代码生成的PHP代码,o(1)在数据库服务器端,因为没有必要像将php生成的代码插入数据库时那样,将表索引更新数千次。
第三种方法,如果你唯一的问题是你在php生成的值中得到重复是将它们放在数组中,然后检查值是否是新的。因为php数组被实现为哈希表,你将获得相当不错的性能。)
如果您选择随机生成PHP代码 - 您将始终面临两个问题 - 第一个 - 无法保证代码不会存在于数据库中,因此您始终需要处理双键问题第二个是因为你需要生成许多代码 - 在sql服务器端将它们插入到db中会非常昂贵,并且因为你有更多的代码 - 会显着减慢脚本的速度