如何为表的主键创建唯一的随机整数ID?

13

我在想是否有人知道一种好的方法来为表的主键创建一个唯一的随机整数id。我正在使用MySQL。该值必须为整数。


3
为什么不只用 AUTO_INCREMENT - RobertPitt
3
因为我想使用该值进行Base62编码,然后将其用作URL中的ID。如果我自动递增,用户可能会明显地了解到URL ID是如何生成的。 - MindGame
4
为了解决你的安全问题,你可以使用AUTO_INCREMENT并添加一个随机生成的数字字段。为了获得你的用户ID,你需要将AUTO ID和随机数相加。然后通过哈希运算(SHA128可以正常工作)来得到你的用户ID。即使随机数字相同(实际上不太可能出现),将其与另一个数字结合并进行哈希运算也将给出你的用户ID。 - g19fanatic
8个回答

14
回应: "因为我想使用该值进行Base62编码,然后将其用作url中的id。如果我自动递增,用户可能会很容易地了解url id是如何生成的。"

如果你的目标是安全性,那么即使使用“随机”生成的数字,也不要使用Base62。

更好的选择是:

  • 不要重复造轮子 — 使用AUTO_INCREMENT
  • 然后使用密码哈希函数+随机生成的字符串(在特定的url中隐藏在数据库中),生成最终的"该url的唯一标识符"

2
感谢您的回复。好的,我使用自动增量,然后使用一个加密哈希函数和一个随机生成的字符串。但我需要我的URL短一些。像tinyurl这样的短网址一样。使用加密哈希将会得到一个“唯一的ID”,但是这个ID会很长,对吧? - MindGame

11

如果你愿意接受建议并且可以实现它,使用 UUID。MySQL 的 UUID() 函数将返回一个 36 个字符的值,可用于 ID。

如果你想使用整数,那么我认为你需要创建一个函数 getRandID(),在 INSERT 语句中使用它。该函数需要使用随机数和现有 ID 的检查来返回一个之前未被使用过的 ID。

检查 MySQL 的 RAND() 函数。


1
你如何确保随机值不会被另一个人同时使用。例如,A和B两个人得到了相同的随机数。他们同时检查它是否存在,结果发现不存在,于是都插入了该值。当然,由于主键约束,其中一个会失败。那么,你会怎么做来解决这个问题呢?锁定表格吗?我从未使用过它,也有点不放心使用它。 - MindGame
1
这就是UUID(通用唯一标识符)的美妙之处。引用维基百科:“UUID的目的是使分布式系统能够在没有重大中央协调的情况下唯一地标识信息。因此,任何人都可以创建一个UUID并将其用于标识某些内容,合理地确信该标识符不会被其他人意外地用于其他任何事情。使用UUID标记的信息因此可以后续合并到单个数据库中,而无需解决名称冲突问题。” - byte_slave
3
@Tesh 当由于唯一约束出现异常时,我会在CATCH中重复执行该函数,并继续执行,直到能够插入具有该随机ID的行。 - Liron Harel

7
你如何生成unique_ids是一个有用的问题,但你似乎对于何时生成它们有一个产生反效果的假设!
我的观点是,你不需要在创建行时生成这些unique id,因为它们本质上与被插入的数据是独立的。
我所做的是预先生成未来使用的unique id,这样我可以自己慢慢地花时间,并绝对保证它们是唯一的,而且在插入时没有任何处理要做。
例如,我有一个包含order_id的订单表。当用户输入订单时,这个id是即时生成的,以1、2、3等增量方式永远递增。用户不需要看到这个内部id。
然后我有另一个表——unique_ids,其中包含(order_id, unique_id)。我有一个程序,每晚运行一次,将足够多的unique_id行预先加载到这个表中,以覆盖可能在接下来的24小时内插入的订单。(如果我一天收到10000个订单,那我就会有问题——但那是一个好问题!)
这种方法保证了唯一性,并将任何处理负载从插入事务中移开,并移到批处理例程中,在那里不影响用户。

3
您可以为表使用AUTO_INCREMENT,但向用户提供加密版本:
encrypted_id:SELECT HEX(AES_ENCRYPT(id,'my-private-key')); id:SELECT AES_DECRYPT(UNHEX(encrypted_id),'my-private-key');

2
这个方案怎么样(使用PHPMySQL):

简短

  1. 生成随机的数字用于用户ID (唯一)
  2. 使用生成的数字插入行作为用户ID
  3. 如果插入的行数等于0,则返回第1点

看起来很复杂?继续阅读。


:

表格:

users (user_id int UNIQUE)

代码:

<?php
// values stored in configuration
$min = 1;
$max = 1000000;

$numberOfLoops = 0;
do {
    $randomNumber = rand($min, $max);

    // the very insert
    $insertedRows = insert_to_table(
        'INSERT INTO foo_table (user_id) VALUES (:number)', 
        array(
            ':number' => $randomNumber
        ));

    $numberOfLoops++;

    // the magic
    if (!isset($reported) && $numberOfLoops / 10 > 0.5) {
        /**
         * We can assume that at least 50% of numbers
         * are already in use, so increment values of
         * $min and $max in configuration.
         */
        report_this_fact();
        $reported = true;
} while ($insertedRows < 1);

  1. 所有的值($min$max0.5)只是为了说明而存在,并没有统计意义。
  2. insert_to_tablereport_this_fact函数不是内置于PHP中。它们也只是为了解释清楚而作为数字提供。

0

有一个 AUTO_INCREMENT 特性。我会使用它。

查看 这里 获取更多示例。


0

AUTO_INCREMENT 是您最好的选择。

这里 有一些示例。

如果需要,您可以调整增量值的起始位置(默认为1)。


0

我的方法适用于32位和64位平台。结果是64位。

function hexstr2decstr($hexstr){
    $bigint = gmp_init($hexstr, 16);
    $bigint_string = gmp_strval($bigint);
    return $bigint_string;
}

function generate_64bitid(){
    return substr(md5(uniqid(rand(), true)), 16, 16);
}

function dbGetUniqueXXXId(){
    for($i = 0; $i < 10; $i++){
        $decstr = hexstr2decstr(generate_64bitid());

        //check duplicate for mysql.tablexxx
        if($dup == false){
            return $decstr;
        }
    }
    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接