3

我阅读了几个与我所要求的主题相似的主题,但似乎没有一个对我很有帮助。

我有一个表单,用户可以在其中生成存储在具有唯一约束的列中的代码。代码是长度为 7 个字符的字符串。用户可以输入一个数字,程序会生成那么多代码,并且可以重复此操作,直到达到最大代码数量。

我的问题是重复值。但不是在输入新条目时数据库中已经存在的值(我成功检查了这些值),但新组(比如 10000)代码中的一些条目(可能)是相同的。所以我的代码在同一个事务中生成两个(或更多)相同的代码,并且数据库中的唯一约束抱怨它。

我想在每个条目后检查数据库,但考虑到我们谈论的是 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好的,我尝试了您的解决方案并且它正在工作(当然),但是后来我遇到了一个新问题-在您的“如果”的“其他”部分中,我正在执行以下操作:

$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'命令中断,即使它应该通过执行'random'调用来改变,然后输入if并中断......

我在这里看不到问题。有人可以给我一些指导吗?

编辑:它有时会挂起,有时不会。我刚刚输入了 10000 个值并通过“else”路径修改了两个条目。我使用日志观察到这一点。

4

3 回答 3

3

已经有一些为您完成了艰苦的工作,允许您选择生成字符串时要使用的“字母表”和字符串的长度。

您的“相同条目”问题称为冲突,无法避免。

编辑因此,类似于 Geo 的建议,我正在使用 aPHP创建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"
  ...
}

添加随机盐每次都会给你随机值

于 2012-12-26T15:51:46.823 回答
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
    }
}
于 2012-12-26T16:10:46.470 回答
1

首先 - 您使用 md5 来生成字符串代码,因为 md5 是十六进制编码的字符串,您正在严重减少可能组合的数量,生成具有 30 个可能字符的随机字符串会给您 210 亿 (10^9) 种可能性,而不是2.68 亿 (10^6) 十六进制字符

另一方面 - 你永远不能创建真正唯一的值(guid-s 是机器唯一的),生成相同值的概率会随着较短的字符串而增加。

我可以采用三种不同的方法(我假设您至少有 30 个无问题的字符),您可以创建唯一的非随机值。假设你有两个计数器生成请求计数和请求计数器。因此,如果用户 1 要求提供 100 个代码,则诸如 user_request_counter-code_counter : '00-00-00_00-00-00-01' to '00-00-01_00-00-03-00' 之类的代码肯定是唯一的(并且是实际上 7 - 每组最多 30 的两位数字可以用单个字符表示(就像十六进制对 16 个字符一样 - 你可以选择任何你喜欢的基数)这将允许你创建 30^4(810,000)个代码到 30^ 3 (27,000) 个用户。这样您就不需要使用昂贵的随机调用,也不需要担心重复代码。

我曾经使用的第二种方法是简单地用随机代码填充数据库,然后将它们分配给用户,这很有用,因为您只需要偶尔执行一次,并且可以离线完成新代码的生成(带有数据库的转储)然后推送到服务器,这样您就可以在 php 代码上的代码生成上获得出色的性能 o(1),在数据库服务器端获得 o(1),因为不需要更新表索引数千次,就像您将 php 生成的代码插入数据库时​​所做的那样。

如果您唯一的问题是您在 php 生成的值中得到重复项,则第三种方法是将它们放入数组中,然后检查这些值是否是新的。由于 php 数组是作为哈希表实现的,因此您将获得相当不错的性能)。

如果您选择在 PHP 中随机生成代码 - 您将始终面临两个问题 - 第一个 - 无法保证代码不会存在于数据库中,因此您将始终需要处理双键问题,第二个是因为您需要生成许多代码 - 将它们插入到 db 在 sql server 端将非常昂贵,并且由于您有更多代码 - 会显着减慢脚本

于 2012-12-26T16:23:11.853 回答