php 为用户创建12位唯一随机数的最佳方式

i2byvkas  于 2023-03-07  发布在  PHP
关注(0)|答案(3)|浏览(323)

问题

我正在尝试向用户表添加一个12长(仅限数值!)的唯一标识符,该标识符将被提供给用户以便用户相互查找。
因为它将被传递给用户,所以它需要与用户表上的自动增量id无关。
我想到的两种方法是:

    • 循环直到获得唯一编号**
do {
    // get a random 12 digit number
    $identifier = str_pad(rand(0, 999999999999), 12, '0', STR_PAD_LEFT);
    // check if it is unique
    $exists = User::where('identifier', $identifier)->exists();
} while ($exists)

return $identifier
    • 缺点**

从理论上讲,它可能会陷入无限循环(尽管几乎不可能)。

    • 相应地调整随机数**
// get all the identifiers as array
$identifiers = User::orderBy('identifier')->pluck('identifier')->toArray();

// random number becomes lower depending on the total users
$my_identifier = rand(0, 999999999999 - count($identifiers));

// increment for all the smaller identifiers
foreach($identifiers as $identifier) {
    if(intval($identifier) > $my_identifier) break;
    $my_identifier ++;
}

return str_pad($my_identifier, 12, '0', STR_PAD_LEFT)
    • 缺点**

虽然这确保了我不会陷入无限循环,但必须循环遍历一个数组,用户计数的长度听起来相当繁重。

问题

我认为这两个解决方案中最好的是第一个,因为无限循环的可能性基本上是不存在的,而第二个解决方案看起来真的很笨重和不切实际。
但是,有没有办法避免这两种解决方案的缺点呢?

ukdjmx9f

ukdjmx9f1#

可以使用UUID(通用唯一标识符)列。UUID是一个128位值,保证在时间和空间上唯一,因此适合生成唯一标识符。
您可以在用户表中创建UUID列,如下所示:

ALTER TABLE users ADD uuid CHAR(36) NOT NULL;

这将在用户表中添加一个名为“uuid”的新列。
要为每个用户生成UUID,可以使用MySQL中的UUID()函数:

INSERT INTO users (name, email, uuid) VALUES ('John Doe', 'johndoe@example.com', UUID());

您也可以使用uniqid()函数在PHP中生成UUID:

$uuid = uniqid('', true); // will generate a 23-character unique identifier

要确保UUID的长度为12位,可以使用substr()函数提取UUID的前12个字符:

$uuid = substr(uniqid('', true), 0, 12); // will generate a 12-digit unique identifier.
ru9i0ody

ru9i0ody2#

您可以使用自动递增的数字,并通过某种算法创建相应的伪随机数,下面是一个示例:

class IdGenerater
{
    private static $RANDOMCHARS = # random
    [
        '2083417956', '4823019567', '8402135679', '4802316759',
        '2483051679', '8421350679', '1503248697', '1053872469',
        '0157824639', '1502784639', '5170248639', '0751248693',
    ];
    private $digits = [];

    public function generate(int $id) : int
    {
        $p = 0;
        while ($id >= 10)
        {
            $rem = $id % 10;
            $this->digits[$p++] = $rem;
            $id = (int)(($id - $rem) / 10);
        }
        $this->digits[$p++] = $id;
        for(; $p < 12; $p++)
            $this->digits[$p] = 0;

        $p = 0; $q = 0;
        for ($i = 0; $i < 12; $i++)
        {
            $p += $this->digits[$i];
            $q = $q * 10 + (int)self::$RANDOMCHARS[$i][$p % 10];
        }
        return $q;
    }
}

自动递增的号码可以从另一个服务生成,结果是一一对应的,但不容易恢复。

$gen = new IdGenerater();
echo $gen->generate(0), PHP_EOL; # 248428110150
echo $gen->generate(666666), PHP_EOL; # 727320824488
pkln4tw6

pkln4tw63#

到目前为止,还没有一个答案来创建一个12位数的标识符,将是随机的,唯一的,"轻",并与用户ID无关。
然而,这个问题上的选项和shingo分享的选项都是完全可行的,在运行了一些测试之后,我的结论是我想得太多了。
在一天结束时,这些方法实际上都没有一个缺点,使他们不可行,你应该选择什么方法使用取决于你的要求。

循环直到我得到一个唯一的编号

| 有限回路|少量循环|与id无关|
| - ------|- ------|- ------|
| 没有|是的|是的|
假设在这个例子中我们有100000个用户。
一个新用户注册,我必须为该用户创建一个新标识符。
在1000000000个可用标识符中,只使用了100000个标识符,while循环循环一次的概率为1/10000。
此外,代码在成为问题之前必须循环数千次。
只有在大部分标识符已经被使用的情况下,这才可能成为一个问题。
您不应该****使用此方法为具有500000个用户的表提供6位标识符。

相应调整随机数

| 有限回路|少量循环|与id无关|
| - ------|- ------|- ------|
| 是的|没有|是的|
我在这段代码中遇到的问题是它"循环太多"。
如果我的表有100000个用户,那么它很容易循环100000次。
然而,在沙箱上测试了一些代码后,我得出结论,这么小的for循环可以在几毫秒内轻松地循环10万次,因此消除了任何关于代码成为性能杀手的担忧。
虽然循环这么多的想法仍然困扰着我,但我会判断这种方法是"最安全的"。

创建伪随机数

| 有限回路|少量循环|与id无关|
| - ------|- ------|- ------|
| 是的|什么事?|没有|
这是shingo提供的解决方案,这个方法的问题是与用户id的一一对应,这是我想避免的。
然而,如果你没有任何问题,这可能是最好的方法。
我认为这种方法还消除了在数据库中存储12位数字的需要,它允许您直接从12位数字代码中获取用户ID。

相关问题