【问题标题】:How to generate unique id in MySQL?如何在 MySQL 中生成唯一 ID?
【发布时间】:2010-11-30 20:31:11
【问题描述】:

我正在使用 PHP 和 MySQL 编写一个脚本,我想获得一个 唯一 ID(由字符串组成:大写和小写) 带数字的字母),例如:gHYtUUi5b。 我在 PHP 中发现很多函数可以生成这样的数字,但我担心如何确保 id 是唯一的!

更新:uuid 很长,我的意思是这样的 id:(P5Dc) an 11 alphanumeric char。

【问题讨论】:

  • 您是否正在寻找与 SQL Server 的 GUID 等效的 MySQL?
  • 你能解释一下你打算用这个做什么吗?
  • 不,我只谈论 ID,例如:gHYtUUi5b,一个短的字母数字字符串。
  • 我使用它通过url访问资源,如果我使用auto_increment,它会很容易建议。
  • 通过默默无闻的安全性:)

标签: php sql mysql


【解决方案1】:

我使用 UUID() 来创建一个唯一值。

示例:

insert into Companies (CompanyID, CompanyName) Values(UUID(), "TestUUID");

【讨论】:

  • thanx 但 uuid 很长,意思是这样的 id:(P5Dc) a 11 alphanumeric char.
  • 它很长,因为它必须确保唯一性:) 如果你真的需要让它变短,请以程序方式生成它,请参阅我的帖子
  • @assaqqaf,你可以使用 UUID_SHORT() 代替。
  • UUID() 的生成是否检查生成的 uuid 是否已存在于表中?换句话说,uuid 在目标表中总是唯一的吗?
【解决方案2】:

程序化方式可以是:

  • 向字段添加唯一索引
  • 在 PHP 中生成随机字符串
  • PHP 中的循环 (while(!DO_THE_INSERT))
    • 生成另一个字符串

注意:

  • 这可能很脏,但具有 与 DBMS 无关的优点
  • 即使您选择使用 DBMS 特定的唯一 ID 生成器功能(UUID 等) 最佳做法是确保字段必须是唯一的,使用索引
  • 统计上根本不执行循环,仅在插入失败时进入

【讨论】:

  • 这是我心中唯一的解决方案,甚至在我发布我的问题之前。是使用资源的方式。同时也不科学。
  • 它是。如果实施得当,它与基于 DBMS 的方法一样安全,资源与生成随机字符串无关,INSERT 失败的概率非常低(当然取决于表维度),所以你的循环实际上是一次操作。
  • 当然,除非您的随机数生成器不是完全随机的。书面竞争条件通常是个坏主意^TM
  • 而不是在插入失败时循环(这可能是由其他原因引起的,从而导致无限循环),我会在找到相同的 ID 时循环生成 ID,然后插入并在插入失败时抛出异常.您只有一个循环的相同统计信息,它更安全,并在插入失败时提供信息(出于其他原因)。
  • 这样你就使用了 2 个数据库操作,不再是原子操作。一旦失败已经发生,最好在循环内部检查失败的原因。
【解决方案3】:

使用UUID function

我不知道您在 PHP 中生成唯一值的程序的来源。如果它是库函数,他们应该保证你的值真的是独一无二的。签入文档。但是,您应该始终使用此功能。例如,如果您使用 PHP 函数生成唯一值,然后您决定使用 MySQL 函数,则可以生成已经存在的值。在这种情况下,将 UNIQUE INDEX 放在列上也是一个好主意。

【讨论】:

    【解决方案4】:

    为了唯一性,我所做的是获取 Unix 时间戳并向其附加一个随机字符串并使用它。

    【讨论】:

    • 您可以使用 microtime 并将其转换为十六进制以获得字符串效果,或者至少在大多数情况下,如果您在 db 中没有大量流量/插入,这将可以正常工作。
    • “我使用它通过 url 访问资源,如果我使用 auto_increment 将很容易建议” .. 记住这一点...如果插入量很高 .. 你可以做另一个解决方法..使用自动增量在数据库中添加普通ID ..在您显示链接的站点上,使用已知密钥对ID进行加密,然后在进行数据库查询之前使用该密钥将其解密。跨度>
    • 如果您已经在使用它.. 我建议保持这种方式.. 我不知道您有多少流量或数据库大小.. 但请记住,varchar 类型查找是比整数查找慢,插入/更新的重新索引也很昂贵。理想情况下,避免每次都计算哈希值..只需在存储此哈希值的数据库中创建另一个字段..从那里向用户显示该哈希值..至少每次需要显示链接时都可以避免加密部分。我个人更喜欢在 php 端而不是 mysql 端增加负载。
    • 我同意你的 80%,但我认为大型网站还使用了其他解决方案,例如:youtube 以保持 id 简单和简短..
    • youtube 将其数据存储在 CDN 上,没有人真正知道它们如何生成唯一 ID .. 但我的猜测是唯一 ID 不仅包含记录 ID,还包含服务器/集群/CDN 寻址.. 所以唯一 ID 基本上是一个以上值的组合,被解密成对记录及其位置的更有意义和更完整的解释。
    【解决方案5】:

    以下仅供参考数字唯一随机id...

    它可以帮助你...

    $query=mysql_query("select * from collectors_repair");
    $row=mysql_num_rows($query);
    $ind=0;
    if($row>0)
    {
    while($rowids=mysql_fetch_array($query))
    {
      $already_exists[$ind]=$rowids['collector_repair_reportid'];
    }
    }
    else
    {
      $already_exists[0]="nothing";
    }
        $break='false';
        while($break=='false'){
          $rand=mt_rand(10000,999999);
    
          if(array_search($rand,$alredy_exists)===false){
              $break='stop';
          }else{
    
          }
        }
    
     echo "random number is : ".$echo;
    

    您可以使用以下代码添加字符 -> $rand=mt_rand(10000,999999) .$randomchar; // assume $radomchar contains char;

    【讨论】:

      【解决方案6】:

      您也可以考虑使用crypt()* 在您的约束中生成一个[几乎可以保证的]唯一ID。

      【讨论】:

        【解决方案7】:

        您可能会喜欢我们这样做的方式。我想要一个看起来“随机”的可逆唯一代码——这是一个相当普遍的问题。

        • 我们输入一个数字,例如 1,942。
        • 左填充成字符串:“0000001942”
        • 将最后两位数字放在前面:“4200000019”
        • 将其转换为数字:4,200,000,019

        我们现在有一个在调用之间变化很大的数字,并且保证小于 10,000,000,000。不错的开始。

        • 将该数字转换为 Base 34 字符串:“2oevc0b”
        • 用“y”替换任何零,用“z”替换任何零:“2oevcyb”
        • 升档:“2OEVCYB”

        选择 base 34 的原因是让我们不用担心 0/O 和 1/l 冲突。现在您有一个简短的随机键,可用于查找 LONG 数据库标识符。

        【讨论】:

        • 我忘记了——如果你在第一次转换回来后将数字加 1,000,000,000,那么你就避免了 YYYYYYYY5 问题(重整基数意味着足够大的非常规基数看起来足够随机) .只是不要忘记在回来的路上减去它:)
        【解决方案8】:

        如何生成 unique_ids 是一个有用的问题 - 但您似乎对何时生成它们做出了适得其反的假设!

        我的意思是,您不需要在创建行时生成这些唯一 ID,因为它们本质上独立于插入的数据。

        我所做的是预先生成唯一 ID 以供将来使用,这样我就可以度过自己的美好时光并绝对保证它们是唯一的,并且在插入时无需进行任何处理。

        例如,我有一个带有 order_id 的订单表。此 ID 在用户输入订单时即时生成,以 1、2、3 等方式永远递增。用户不需要看到这个内部 id。

        然后我有另一个表 - 带有 (order_id, unique_id) 的 unique_ids。我有一个每天晚上运行的例程,它用足够的 unique_id 行预加载此表,以覆盖未来 24 小时内可能插入的订单。 (如果我在一天内收到 10000 个订单,我会遇到问题 - 但这是一个很好的问题!)

        这种方法保证了唯一性,并将任何处理负载从插入事务转移到批处理例程中,不会影响用户。

        【讨论】:

        • 真的不明智!使用服务器硬件生成随机字符串是如此之快,以至于您真的经历了太多
        • "我所做的是预先生成唯一的 id 以供将来使用,这样我就可以度过自己的美好时光并绝对保证它们是独一无二的"你怎么知道 id 不存在于数据库,除非您在插入时检查?
        • 我真的很喜欢这种方法。感谢您的建议
        • 如果要使用它,他可以使用一种方法在键表中创建键并将其/它们返回给执行插入的函数。这将消除夜间密钥生成的上限。将这些包装到事务中以避免累积未使用的密钥。
        • 回复 Steven Byks ...您可以保证唯一性,因为在生成新 id 时,您知道订单表中已经存在的所有 id 以及您已经准备好的所有 id在您的暂存/充值区域,或任何您想称呼它的地方。这样做的部分原因是这些随机 id 并不是真正随机的——你必须检查它们是否与你已经拥有的不同,这可能需要一些额外的时间。无需在插入时检查 - 您已经保证了唯一性 - 但是在该数据库列上放置一个唯一键以防出现错误!
        【解决方案9】:
         <?php
            $hostname_conn = "localhost";
            $database_conn = "user_id";
            $username_conn = "root";
            $password_conn = "";
             $conn = mysql_pconnect($hostname_conn, $username_conn,   $password_conn) or trigger_error(mysql_error(),E_USER_ERROR); 
           mysql_select_db($database_conn,$conn);
           // run an endless loop      
            while(1) {       
            $randomNumber = rand(1, 999999);// generate unique random number               
            $query = "SELECT * FROM tbl_rand WHERE the_number='".mysql_real_escape_string ($randomNumber)."'";  // check if it exists in database   
            $res =mysql_query($query,$conn);       
            $rowCount = mysql_num_rows($res);
             // if not found in the db (it is unique), then insert the unique number into data_base and break out of the loop
            if($rowCount < 1) {
            $con = mysql_connect ("localhost","root");      
            mysql_select_db("user_id", $con);       
            $sql = "insert into tbl_rand(the_number) values('".$randomNumber."')";      
            mysql_query ($sql,$con);        
            mysql_close ($con);
            break;
            }   
        }
          echo "inserted unique number into Data_base. use it as ID";
           ?>
        

        【讨论】:

          【解决方案10】:

          crypt() 按照建议将盐存储在某个配置文件中,从 1 开始盐,如果发现重复,则移动到下一个值 2。您可以使用 2 个字符,但这将为您提供足够的盐组合。

          您可以从openssl_random_pseudo_bytes(8) 生成字符串。因此,当使用crypt() 运行时,这应该给出随机和短字符串(11 个字符)。

          从结果中删除盐,如果您在每次随机失败时更改盐,则只有 11 个字符应该足够随机用于 100+ 百万。

          【讨论】:

            【解决方案11】:

            如果你使用5.7.4以上版本的MySQL,可以使用新添加的RANDOM_BYTES函数:

             SELECT TO_BASE64(RANDOM_BYTES(16));
            

            这将产生一个随机字符串,例如GgwEvafNLWQ3+ockEST00A==

            【讨论】:

              【解决方案12】:

              这会生成随机 id:

              CREATE TABLE Persons (
                  ID Integer PRIMARY KEY AUTOINCREMENT,
                  LastName varchar(255) NOT NULL,
                  FirstName varchar(255),
                  Age int
              );
              

              【讨论】:

              • 这是答案吗?
              • 这不会产生 随机 id,它会产生增量 id。
              【解决方案13】:
              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 "";
                  DECLARE newUniqueValue 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 ;
              

              并将存储过程调用为 GenerateUniqueValue('tableName','columnName')。这每次都会给你一个 8 位数的唯一字符。

              【讨论】:

                【解决方案14】:

                要获得独特且随机的令牌,您可以加密您的主密钥,即:

                SELECT HEX(AES_ENCRYPT(your_pk,'your_password')) AS 'token' FROM your_table;

                这已经足够好了,而且它是可逆的,因此您不必将该令牌存储在您的表中,而是生成它。

                另一个优势是,一旦您从该令牌解码您的 PK,您就不必对您的表进行繁重的全文搜索,而只需简单快速的 PK 搜索。

                不过有一个小问题。 MySql 支持不同的block encryption modes,如果更改将完全改变您的令牌空间,使旧令牌无用......

                为了克服这个问题,可以在生成令牌之前设置该变量,即:

                SET block_encryption_mode = 'aes-256-cbc';

                不过有点浪费...解决方案是在令牌上附加一个加密模式使用标记:

                SELECT CONCAT(CONV(CRC32(@@GLOBAL.block_encryption_mode),10,35),'Z',HEX(AES_ENCRYPT(your_pk,'your_password'))) AS 'token' FROM your_table;
                

                如果您希望将该令牌保留在INSERT 上的表中,则可能会出现另一个问题,因为要生成它,您需要知道尚未插入的记录的primary_key...当然您可能只需要INSERT 和然后 UPDATELAST_INSERT_ID() 但同样 - 还有一个更好的解决方案:

                INSERT INTO your_table ( token )
                SELECT CONCAT(CONV(CRC32(@@GLOBAL.block_encryption_mode),10,35),'Z',HEX(AES_ENCRYPT(your_pk,'your_password'))) AS 'token'
                FROM information_schema.TABLES 
                WHERE  TABLE_SCHEMA = DATABASE() AND TABLE_NAME = "your_table";
                

                此解决方案的最后一个但并非最不重要的优势是您可以轻松地将其复制为 php、python、js 或您可能使用的任何其他语言。

                【讨论】:

                  【解决方案15】:

                  你可以使用 Twitter 的雪花。

                  简而言之,它根据时间、服务器 id 和一个序列生成一个唯一的 id。它生成一个 64 位的值,因此它非常小并且适合 INT64。它还允许对值进行正确排序。

                  https://developer.twitter.com/en/docs/basics/twitter-ids

                  总而言之,它允许多台服务器、高并发、排序值以及所有这些都在 64 位中。

                  这里是 MySQL 的实现

                  https://github.com/EFTEC/snowflake-mysql

                  它由一个函数和一个表组成。

                  【讨论】:

                    【解决方案16】:

                    使用它

                        $info = random_bytes(16);
                        $info[6] = chr(ord($info[6]) & 0x0f | 0x40); 
                        $info[8] = chr(ord($info[8]) & 0x3f | 0x80); 
                        $result =vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($info), 4));
                        return $result;
                    

                    【讨论】:

                      猜你喜欢
                      • 2011-06-20
                      • 1970-01-01
                      • 2018-05-04
                      • 2023-03-15
                      • 2013-03-11
                      • 1970-01-01
                      • 2016-09-27
                      • 2019-04-29
                      • 2018-10-20
                      相关资源
                      最近更新 更多