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

问题描述 投票:0回答:3

问题

我正在尝试向用户表添加一个 12 digit 长(仅限数值!)的唯一标识符,该标识符将提供给用户以找到彼此。
因为它将交给用户,所以它需要与用户表上的自动递增 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)

缺点
虽然这确保我不会遇到无限循环,但必须循环遍历数组,但我的用户计数的长度听起来很重。

问题

我认为这两个中最好的解决方案是第一个,因为无限循环的可能性基本上不存在,而第二个解决方案看起来非常沉重且不切实际。

但是有没有办法避免这两种解决方案的缺点呢?

php random unique uniqueidentifier
3个回答
1
投票

您可以使用 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', '[email protected]', 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.

1
投票

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

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

0
投票

到目前为止,还没有创建一个 12 位标识符的答案,该标识符将是随机的、唯一的、“轻量级”的,并且与用户 ID 无关。

然而问题上的选项和shingo分享的选项都是完全可行的,经过一些测试后我的结论是我想多了。

归根结底,这些方法实际上都没有使它们不可行的缺点,您应该根据您的要求选择使用哪种方法。

循环直到我得到一个唯一的数字

有限循环 几个循环 与id无关

假设我们有 100000 个用户。
一个新用户注册,我必须为该用户创建一个新标识符。

在 1000000000 个可用标识符中仅使用了 100000 个标识符,即使循环一次也有 1/10000 的机会循环。
此外,代码必须循环数千次才能成为问题。

如果已经使用了很大比例的标识符,这只会成为一个问题。
你应该 not 使用这种方法给一个有 500000 个用户的表一个 6 位标识符。

相应地调整随机数

有限循环 几个循环 与id无关

我对这段代码的问题是它“循环太多”。
如果我的表有 100000 个用户,它很容易就会循环 100000 次。

然而,在沙盒上测试了一些代码后,我得出的结论是,这么小的 for 循环可以在几毫秒内轻松循环 100000 次,因此无需担心代码会成为性能杀手。

虽然循环这么多的想法仍然困扰着我,但我认为这种方法是“最安全的”。

创建一个伪随机数

有限循环 几个循环 与id无关
是吗?

这是shingo提供的解决方案。 这种方法的问题是与用户 id 的一一对应,这是我想避免的。

但是,如果您对此没有任何问题,这可能是最好的方法。
我认为这种方法还消除了在数据库中存储 12 位数字的需要,它允许您直接从 12 位代码中获取用户 ID。

© www.soinside.com 2019 - 2024. All rights reserved.