我正在尝试向用户表添加一个 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)
缺点
虽然这确保我不会遇到无限循环,但必须循环遍历数组,但我的用户计数的长度听起来很重。
我认为这两个中最好的解决方案是第一个,因为无限循环的可能性基本上不存在,而第二个解决方案看起来非常沉重且不切实际。
但是有没有办法避免这两种解决方案的缺点呢?
您可以使用 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.
您可以使用自动递增数并通过某种算法创建相应的伪随机数,这是一个示例:
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
到目前为止,还没有创建一个 12 位标识符的答案,该标识符将是随机的、唯一的、“轻量级”的,并且与用户 ID 无关。
然而问题上的选项和shingo分享的选项都是完全可行的,经过一些测试后我的结论是我想多了。
归根结底,这些方法实际上都没有使它们不可行的缺点,您应该根据您的要求选择使用哪种方法。
有限循环 | 几个循环 | 与id无关 |
---|---|---|
不 | 是 | 是 |
假设我们有 100000 个用户。
一个新用户注册,我必须为该用户创建一个新标识符。
在 1000000000 个可用标识符中仅使用了 100000 个标识符,即使循环一次也有 1/10000 的机会循环。
此外,代码必须循环数千次才能成为问题。
如果已经使用了很大比例的标识符,这只会成为一个问题。
你应该 not 使用这种方法给一个有 500000 个用户的表一个 6 位标识符。
有限循环 | 几个循环 | 与id无关 |
---|---|---|
是 | 不 | 是 |
我对这段代码的问题是它“循环太多”。
如果我的表有 100000 个用户,它很容易就会循环 100000 次。
然而,在沙盒上测试了一些代码后,我得出的结论是,这么小的 for 循环可以在几毫秒内轻松循环 100000 次,因此无需担心代码会成为性能杀手。
虽然循环这么多的想法仍然困扰着我,但我认为这种方法是“最安全的”。
有限循环 | 几个循环 | 与id无关 |
---|---|---|
是 | 是吗? | 不 |
这是shingo提供的解决方案。 这种方法的问题是与用户 id 的一一对应,这是我想避免的。
但是,如果您对此没有任何问题,这可能是最好的方法。
我认为这种方法还消除了在数据库中存储 12 位数字的需要,它允许您直接从 12 位代码中获取用户 ID。