簡體   English   中英

如何在 MySQL 中生成唯一 ID?

[英]How to generate unique id in MySQL?

我正在使用 PHP 和 MySQL 編寫腳本,我想獲得一個唯一的 id(由一個字符串組成:大寫字母和帶數字的小寫字母),例如: gHYtUUi5b 我發現 PHP 中有很多函數可以生成這樣的數字,但我擔心如何確保 id 是唯一的!

更新:uuid 很長,我的意思是這樣的 id:(P5Dc) 一個 11 個字母數字字符。

我使用 UUID() 創建一個唯一值。

例子:

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

您可能會喜歡我們的做法。 我想要一個看起來“隨機”的可逆唯一代碼 - 一個相當普遍的問題。

  • 我們采用一個輸入數字,例如 1,942。
  • 將其左填充為字符串:“0000001942”
  • 將最后兩位數字放在前面:“4200000019”
  • 將其轉換為數字:4,200,000,019

我們現在有一個在調用之間變化很大的數字,並且保證小於 10,000,000,000。 不是一個糟糕的開始。

  • 將該數字轉換為 Base 34 字符串:“2oevc0b”
  • 用“y”替換任何零,用“z”替換任何零:“2oevcyb”
  • 升檔:“2OEVCYB”

選擇基數 34 的原因是我們不用擔心 0/O 和 1/l 沖突。 現在您有一個簡短的隨機鍵,可用於查找 LONG 數據庫標識符。

一種編程方式可以是:

  • 向字段添加唯一索引
  • PHP生成隨機字符串
  • PHP中的循環(while(!DO_THE_INSERT))
    • 生成另一個字符串

筆記:

  • 這可能很臟,但具有與DBMS 無關的優勢
  • 即使您選擇使用 DBMS 特定的唯一 ID 生成器函數(UUID 等),最好的做法使用索引確保字段必須是 UNIQUE
  • 循環在統計上根本不執行,僅在插入失敗時進入

如果使用5.7.4以上版本的MySQL,可以使用新增的RANDOM_BYTES函數:

 SELECT TO_BASE64(RANDOM_BYTES(16));

這將產生一個隨機字符串,例如GgwEvafNLWQ3+ockEST00A==

您如何生成 unique_ids 是一個有用的問題 - 但您似乎對何時生成它們做出了適得其反的假設!

我的觀點是,您不需要在創建行時生成這些唯一 ID,因為它們本質上與插入的數據無關。

我所做的是預先生成唯一的 id 以備將來使用,這樣我就可以享受自己的甜蜜時光並絕對保證它們是唯一的,並且在插入時無需進行任何處理。

例如,我有一個帶有 order_id 的訂單表。 這個 id 是在用戶輸入訂單時即時生成的,永遠遞增 1,2,3 等。 用戶不需要看到這個內部 ID。

然后我有另一個表 - unique_ids with (order_id, unique_id)。 我有一個每天晚上運行的例程,它使用足夠的 unique_id 行預加載此表,以涵蓋可能在接下來的 24 小時內插入的訂單。 (如果我在一天內收到 10000 個訂單,我就會遇到問題——但這將是一個很好的問題!)

這種方法保證了唯一性,並將任何處理負載從插入事務轉移到批處理例程中,在那里它不會影響用戶。

使用UUID 函數

我不知道您在 PHP 中生成唯一值的程序的來源。 如果是庫函數,他們應該保證你的值真的是獨一無二的。 簽入文檔。 但是,您應該一直使用此功能。 例如,如果您使用 PHP 函數生成唯一值,然后您決定使用 MySQL 函數,則可以生成已存在的值。 在這種情況下,將 UNIQUE INDEX 放在列上也是一個好主意。

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 位數的唯一字符。

為了唯一性,我所做的是獲取 Unix 時間戳並在其中附加一個隨機字符串並使用它。

以下僅供參考數字唯一隨機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; $rand=mt_rand(10000,999999) .$randomchar; // assume $radomchar contains char;

 <?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";
   ?>

crypt()按照建議將鹽存儲在某個配置文件中,從 1 開始 salt,如果發現重復移動到下一個值 2。您可以使用 2 個字符,但這將為您提供足夠的鹽組合。

您可以從openssl_random_pseudo_bytes(8)生成字符串。 因此,當使用crypt()運行時,這應該給出隨機和短字符串(11 個字符crypt()

從結果中刪除鹽分,如果您在每次隨機失敗時更改鹽分,那么只有 11 個字符應該足夠隨機 100+ 百萬。

要獲得獨特且隨機的令牌,您只需加密主鍵即可:

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

這已經足夠好了,而且它是可逆的,因此您不必將該令牌存儲在您的表中,而是生成它。

另一個優點是,一旦您從該令牌中解碼出您的 PK,您就不必對您的表格進行大量的全文搜索,而是進行簡單快速的 PK 搜索。

不過有一個小問題。 MySql 支持不同的塊加密模式,如果更改這些模式將完全改變您的令牌空間,使舊令牌無用......

為了克服這個問題,可以在生成令牌之前設置該變量,即:

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然后使用LAST_INSERT_ID() UPDATE再次 - 有一個更好的解決方案:

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 或您可能使用的任何其他語言中。

您還可以考慮使用crypt() *在您的限制內生成一個 [幾乎保證] 唯一 ID。

你可以使用 Twitter 的雪花。

簡而言之,它根據時間、服務器 id 和序列生成唯一的 id。 它生成一個 64 位值,因此它非常小並且適合 INT64。 它還允許正確排序值。

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

總而言之,它允許多台服務器,高並發,排序值,並且都是64位的。

這是MySQL的實現

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

它由一個函數和一個表組成。

用它

    $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;

這會生成隨機 ID:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM