概述:
下面的函数吐出一个随机ID。我用它来提供一个确认别名来识别记录。然而,我不得不检查碰撞(无论多么不可能),因为我们只使用了五位数的长度。加上下面列出的允许字符,它有大约3300多万个组合。最终,我们将获得大约500万张记录,因此碰撞成为一个问题。
问题:
检查重复别名效率低且资源繁重。500万条记录需要搜索。尤其是当不同的用户同时进行此搜索时。
我的问题:
有没有办法"自动递增"这个函数允许的组合?这意味着我只需要搜索上一条记录的别名,然后转到下一个组合?
已确认的限制:
我意识到代码与下面的函数会有很大的不同。我还意识到mysql有一个数字ID的自动递增功能,但该项目需要一个五位数的别名,允许字符为"23456789ABCDEFGHJKLMNPQRSTUVXYZ"。在那个问题上我无能为力。
我的当前功能:
public function random_id_gen($length)
{
$characters = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
$max = strlen($characters) - 1;
$string = '';
for ($i = 0; $i < $length; $i++) {
$string .= $characters[mt_rand(0, $max)];
}
return $string;
}
为什么不在别名列上创建一个唯一的索引?
CREATE UNIQUE INDEX uniq_alias ON MyTable(alias);
此时,您可以尝试插入/更新,如果返回错误,请生成一个新的别名,然后重试。
您真正需要做的是从基数10转换为基数strlen($characters)
。
PHP附带了一个内置的base_convert
函数,但它并不能完全满足您的要求,因为它将使用数字0、1和字母"o",而您的版本中没有这些数字。因此,您需要一个函数来将base_convert
的值从/映射到您的值:
function map_basing($number, $from_characters, $to_characters) {
if ( strlen($from_characters) != strlen($to_characters)) {
// ERROR!
}
$mapped = '';
foreach( $ch in $number ) {
$pos = strpos($from_characters, $ch);
if ( $pos !== false ) {
$mapped .= $to_characters[$pos];
} else {
// ERROR!
}
}
return $mapped;
}
现在你有了:
public function next_id($last_id)
{
$my_characters = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
$std_characters ='0123456789abcdefghijklmnopqrstuv';
// Map from your basing to the standard basing.
$mapped = map_basing($last_id, $my_characters, $std_characters);
// Convert to base 10 integer and increment.
$intval = base_convert($mapped, strlen($my_characters), 10);
$intval++;
// Convert to standard basing, then to our custom basing.
$newval_std = base_convert($intval, 10, strlen($my_characters));
$newval = map_basing($newval_std, $std_characters, $my_characters);
return $newval;
}
可能有一些语法错误,但你应该了解它的要点。
您可以滚动自己的自动增量。不过,这可能会相当低效,因为你必须弄清楚在这个过程中你的增量在哪里。例如,如果您将随机字符串中的位置指定为整数,并以(0)(0)开始,则相当于22222作为ID。然后,要获得下一个值,只需将最后一个值增加到(0)。如果最后一个达到你的字符串长度,那么将其设为0,并将第二个增加到最后一个,等等……这不是完全随机的,但它会增加并且是唯一的。