基于php实现多进制转换与唯一码生成的探索

来源:互联网 时间:2018-01-19

最近要做一个兑换码生成的功能,之前有做过32位唯一码生成器,但是在业务需求中,32位的兑换码有些过长了,用户在应用内填写的时候会比较麻烦,不是很友好,倒是可以做成二维码的形式扫一下就行了,但是业务中还是存在输入兑换码的行为,所以本篇主要是关于以尽量短的字符来生成唯一码,同时要保证唯一性以及生成机制复用性(也就是利用这套机制可以生成不同种类的兑换码)的探索



以下示例代码均基于 TPRCMS
编写


探索一: 进制转换

生成的32位唯一码是16进制的一串字符,我就在想是不是可以通过提高进制来缩短字符串长度,所以有了如下的代码



代码地址: 多进制转换器ConvertLogic



其中关于10进制与62进制互转的部分,参考了 《PHP 10进制与62进制互转,可用于生成短网址》


实例代码


$uuid = "cd5fd2cfeb40aafe060f4d9597348be7";
$str = ConvertLogic::convert( $uuid, 16, 62);

输出


string(32) "cd5fd2cfeb40aafe060f4d9597348be7"
string(22) "6fxdxREtzxq6qNdSghGm7t"

经过转换后发现,长度最多压缩到21~22位,感觉还是有些长


探索二: uniqid()转62进制

uniqid()可以生成一个13位以上的16进制唯一码,将它转为62进制,会到一个更短的字符串


实验代码


$uuid = uniqid('code');
$resule = ConvertLogic::convert( $uuid, 16, 62);
//输出
//string(17) "5a5c5b182386"
//string(12) "7hoyVkRTi "

通过多次生成,从结果观察来看,有以下几个不足:


1.用这种方法生成的一批兑换码,只有后5位是变动的,前面的都不变,可能会造成结果比较好猜,容易被爆破试出来。


2.唯一性不足。在批量并发多机器生成的时候,很难保证唯一性


探索三: 伪随机生成12位字符串,用redis保证唯一性

示例代码


//$category_uniq是类目的hash,下面这句代码主要目的是保证同一类目下不存在相同的兑换码
RedisService::redis()->switchDB()->hSetNx('code_hash_list_'.$category_uniq, $code, $category_uniq);
//如果同类目下已存在相同的兑换码,会返回false

伪随机字符生成代码


private static function uniq($uniq, $times)
{
if ($times == 0) {
return $uniq;
}
$temp = substr(ConvertLogic::convert(uniqid(), 16, 62), 7);
$uniq = $uniq . $temp;
$times--;
return self::uniq($uniq, $times);
}

通过串行100万次随机生成发现,一开始差不多是4万分之一的重复率,之后会逐渐上升


探索四: redis"领票",用事务操作保证计数器的准确,一个兑换码领一个票

4位票号 + 12位伪随机码


4位票号的目的是为了保证唯一性,伪随机码是为了防止被轻易试出正确值。


这样即便知道了前面4位是票号,加一就行,但是后面12位可就难试了。


而且4位的票号可以支持一个类目有14538000个兑换码(61进制,"ZZZZ"-"1000"),即便是票号不够了,那票码长度+1即可


这个机制还有一个好处就是,可以根据情况自由修改兑换码长度


比如,只需生成几百个码,而且对安全性没有太多要求,那就可以只需要“2位票码+4位随机符”就可以了



领票代码

private static function ticket($category_uniq , $baseCount){
$key = 'code_ticket_'.$category_uniq;
RedisService::redis()->switchDB()->watch('code_ticket_'. $category_uniq);
$count = RedisService::redis()->switchDB()->multi()
->incr($key)
->exec();
if($count === false){
return self::ticket($category_uniq, $baseCount);
}
$ticket = $baseCount + $count;
return $ticket;
}


兑换码批量生成完整逻辑代码 : CodeLogic

相关阅读:
Top