基于php实现多进制转换与兑换码生成的探索
最近要做一个兑换码生成的功能,之前有做过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.用这种方法生成的一批兑换码,只有后几位是变动的,生成间隔很近的话,只有最后2位不一样,前面的都一样,这样可能会造成结果比较好猜,容易被试出来。
2.唯一性不足。在批量并发多机器生成的时候,很难保证唯一性
探索三: 随机生成12位字符串,用redis保证唯一性
示例代码
//$category_uniq是类目的hash,下面这句代码主要目的是保证同一类目下不存在相同的兑换码
RedisService::redis()->switchDB()->hSetNx('code_hash_list_'.$category_uniq, $code, $category_uniq);
//如果同类目下已存在相同的兑换码,会返回false
随机字符生成代码
/**
* 随机字符串生成器
* @param $length
* @param string $strPol
* @return null|string
*/
public static function getRandChar($length = 15,$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"){
$str = null;
$max = strlen($strPol)-1;
for($i=0;$i<$length;$i++){
$str.=$strPol[rand(0,$max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
}
return $str;
}
通过串行100万次随机生成发现,随着数据的逐渐增多,重复的概率会逐渐上升,这就意味重新生成的次数越来越多。
探索四: 利用redis计数器"领票",用事务操作保证计数器的准确,一个兑换码领一个票
4位票号 + 12位随机码
4位票号的目的是为了保证唯一性,随机码是为了防止被轻易试出正确值。
这样即便知道了前面4位是票号,加一就行,但是后面12位可就难试了。
而且4位的票号可以支持一个类目有14538000个兑换码(62进制,"ZZZZ"-"1000"),即便是票号不够了,那票码长度+1即可
这个机制还有一个好处就是,可以根据情况自由修改兑换码长度
比如,现业务只需生成几百个码,而且对安全性没有太多要求,那就可以只需要“2位票码+4位随机符”就可以了
- 领票代码
private static function ticket($category_uniq , $baseCount){
$key = 'code_ticket_'.$category_uniq;
RedisService::redis()->switchDB()->watch($key);
$count = RedisService::redis()->switchDB()->multi()
->incr($key)
->exec();
if($count === false){
return self::ticket($category_uniq, $baseCount);
}
$ticket = $baseCount + $count;
return $ticket;
}
兑换码批量生成完整逻辑代码 : CodeLogic
拓展思考
- 短网址
利用票号转62进制的思路,其实也可以做短网址
仅需要6位就可以满足500多亿的网址
而每次要新增短网址,只需要到后端领一个票码,并建立票码和真实网址的对应关系(KV)就可以了
转自:https://hanxv.cn/archives/111.html?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
热门日志
分类
- Django(4)
- ssdb(1)
- Mac(7)
- C(1)
- memcache(1)
- Python(32)
- Vim(8)
- sed(2)
- ansible(3)
- awk(4)
- shell(3)
- about(1)
- git(9)
- bat(4)
- svn(0)
- docker(1)
- Tornado(1)
- go(2)
- 架构(18)
- Vue(1)
- game(2)
- Html(6)
- Java(8)
- Mysql(37)
- Ajax(2)
- Jsp(1)
- Struts(8)
- Linux(72)
- JavaScript(39)
- Staruml(0)
- Mouth(1)
- Php(102)
- Windows(8)
- Message(48)
- Lua(10)
- Compute(1)
- Redis(7)
- Nginx(12)
- Jquery(1)
- Apache(1)
- cocos2d-x(8)