【问题标题】:PHP/MySQL - Best way to create unique random string?PHP/MySQL - 创建唯一随机字符串的最佳方法?
【发布时间】:2011-12-15 03:33:39
【问题描述】:

如何在 MySQL 中创建一个随机的唯一字符串?

当我需要在 PHP 中创建一个随机字符串时,我会使用这个函数:

public function generateString($length)
{   
    $charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    for($i=0; $i<$length; $i++) 
        $key .= $charset[(mt_rand(0,(strlen($charset)-1)))]; 

    return $key;
}

然后我会将生成的字符串存储在 MySQL 数据库中。

确保生成的随机字符串对于为数据库中的其他条目创建的所有其他随机字符串唯一的最佳方法是什么?

也许是这样的?

while(++$i < 100)
{
  //query db with random key to see if there is a match

  //if no match found break out of loop
  break;

}

这看起来很乱而且很长,我可能会多次访问数据库。如何快速确定我的新随机字符串是唯一的?

【问题讨论】:

标签: php mysql


【解决方案1】:
DELIMITER $$

USE `temp` $$

DROP PROCEDURE IF EXISTS `GenerateUniqueValue`$$

CREATE PROCEDURE `GenerateUniqueValue`(IN tableName VARCHAR(255),IN columnName VARCHAR(255)) 
BEGIN
    DECLARE uniqueValue VARCHAR(8) DEFAULT "";
    WHILE LENGTH(uniqueValue) = 0 DO
        SELECT CONCAT(SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1),
                SUBSTRING('ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', RAND()*34+1, 1)
                ) INTO @newUniqueValue;
        SET @rcount = -1;
        SET @query=CONCAT('SELECT COUNT(*) INTO @rcount FROM  ',tableName,' WHERE ',columnName,'  like ''',@newUniqueValue,'''');
        PREPARE stmt FROM  @query;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    IF @rcount = 0 THEN
            SET uniqueValue = @newUniqueValue ;
        END IF ;
    END WHILE ;
    SELECT uniqueValue;
    END$$

DELIMITER ;

使用此存储过程并将此存储过程称为

Call GenerateUniqueValue('tableName','columnName')

【讨论】:

    【解决方案2】:

    唯一的随机字符串可以用作字符键或标记来识别数据库记录并检查数据库表并提供唯一键和存储$refer_by变量。

    define('DB_SERVER', "localhost");
    define('DB_USER', "root");
    define('DB_PASS', "");
    define('DB_DATABASE', "student");
    $con = mysqli_connect(DB_SERVER, DB_USER, DB_PASS, DB_DATABASE);
    
    function refercode()
    {
        $string = '';
        $characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
        $max = strlen($characters) - 1;
        for ($i = 0; $i < 6; $i++) {
            $string .= $characters[mt_rand(0, $max)];
        }
        $refer = "select * from user_detail where refer_code = '".$string."' ";
        $coderefertest = mysqli_query($con,$refer);
    
        if(mysqli_num_rows($coderefertest)>0)
        {
            return refercode();
        }
        else
        {
            return $string;
        }
    }
    $refer_by = refercode();
    
    【解决方案3】:

    如果您想出于安全目的使用这些字符串,则应使用openssl_random_pseudo_bytes,这将表明 PHP 是否能够使用强大的算法来生成它:

    输出需要一些清洁。请查看this question 了解更多信息。

    【讨论】:

      【解决方案4】:

      我通常使用:

      SELECT LEFT(MD5(id), 8)
      

      根据需要变体:

      SELECT LEFT(UUID(), 8)
      
      SELECT LEFT(MD5(RAND()), 8)
      

      【讨论】:

        【解决方案5】:

        SET rand_str = SUBSTRING(MD5(NOW()),1,$LENGTH); -- 根据 MD5,LENGTH 为 1 到 32

        一些例子如下:

        SET rand_str = SUBSTRING(MD5(NOW()),1,5); -- 5 个字符的字符串

        SET rand_str = SUBSTRING(MD5(NOW()),1,15); -- 15 个字符的字符串

        【讨论】:

          【解决方案6】:

          我会让这个 id 的列在你的数据库中是唯一的。然后你可以做这样的事情来防止碰撞:

              $row_count = 0;
              while ($row_count == 0) {
                  error_reporting(0);
                  $id_string = substr(uniqid(), 0, 10);
          
                  $sql = "UPDATE <table> SET unique_id = :unique_id WHERE <something true>";
                  $query = $this->db->prepare($sql);
                  $query->execute(array(':unique_id' => $unique_id));
                  $row_count = $query->rowCount();
              }
          

          当然,它可能需要多次尝试查询,但这样您就知道它在您的数据库中是唯一的。 error_reporting(0) 行用于抑制任何可能打开的警告。 PHP 的 uniqid() 也不是最独特的生成器,但您可以轻松地将其换成自己的,或者只是在这里和那里受到潜在冲突的影响。

          【讨论】:

            【解决方案7】:

            幸运的是,数据库已经具备创建唯一 ID(数字)的能力 - 我建议采用我们采用的方法,即在逐渐增加的数字 ID 和字母数字 ID 之间创建双向转换。让它成为双向可确保字母数字“随机”版本也是唯一的,而无需显式测试它们。事实上,我只将数字版本存储在数据库中(因为您可以通过 SERIAL 列免费获得它)并且只打印 alpha 版本。

            此示例生成 7 个字节的 ID,但可以对方法进行微调以适应几乎任何情况。

            见:How to generate unique id in MySQL?

            【讨论】:

              【解决方案8】:

              假设字符集 a-z, A-Z, 0-9 中的 10 个字符意味着有 (26 + 26 + 10)10 = 8.39299366 × 1017 种可能的组合。要计算碰撞的几率……只需上述数字的 1/x。所以我不会担心两次得到相同的字符串。即使再次得到相同的字符串,我也会在循环中再次运行该函数,唯一的退出条件是找到一个唯一的字符串。

              【讨论】:

              • 您的碰撞概率并不完全正确 - 您计算的是 1-1 碰撞,而不是集合。对于小型集合,您发生冲突的机会大约是n(n-1)/N,其中n 是集合的大小,N 是您的 8.329x10^17。由此得出的结论是,您的碰撞机会在大约sqrt(N) - 10 ^ 8 时变得不可忽略,因此如果您有很多(但不是完全不合理的大量)数据,您可能会发生碰撞。在这种情况下,最好的解决方案是稍微增加您的密钥长度,或者在使用该值之前进行检查。
              • @Michael:我认为,鉴于我已经有一百万个唯一字符串,碰撞的概率仍然是万亿分之一 (1000000/62^10)。
              • 我同意添加一个字符串时发生冲突的几率是 1M/(10^17),因为您已经有 1M 个唯一字符串。但是您必须这样做 100 万次。这意味着您的总体碰撞几率是(大致)1M x (1M/(10^17))
              • @SalmanA 你会设置一个最大重试次数参数吗?例如。最多尝试1000 次生成号码?或者你会把你的代码放在一个无限循环中?
              • 我会把它放在一个无限循环中,如果循环执行 1000 次......甚至 3 次,就会抛出异常。
              【解决方案9】:

              看看uniqid 函数和pecl uuid extension。任何一个都可以用作生成 guid 的基础,但如果您计划拥有一个集群,您将需要确保有额外的东西来确保两台服务器不会生成相同的 id。将每个服务器配置作为前缀或后缀添加到 id 就足以解决该问题。

              【讨论】:

                【解决方案10】:

                为什么不直接使用内置函数来生成唯一标识符?这样你就不用担心重复了。

                PHP 和 MySQL 都有自己的。

                PHP:uniqid()

                MySQL:UUID()

                【讨论】:

                • 默认情况下 uniquid 给出 13 个字符,我需要 10 个。所以如果我砍掉最后 3 个字符,会不会留下重复的机会?
                • 为什么必须是 10 个字符?
                • 另一方面,这个问题有一些很好的答案。 stackoverflow.com/questions/307486/short-unique-id-in-php
                • 这是我正在构建的网络应用程序的要求。
                • 那么我认为您陷入了滚动自己的解决方案并手动检查重复项的困境。
                猜你喜欢
                • 1970-01-01
                • 2014-10-20
                • 1970-01-01
                • 1970-01-01
                • 2011-01-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多