Redis生成无规律不重复的纯数字券码

需求描述
在开发优惠券系统或票务系统的时候,经常要生成纯数字码,券码要求:12位纯数字,无规律,不重复。

下面我提供一种思路,利用redis的List数据类型,Lpop+Rpush 维护一个1万个码的队列

队列数据结构
保持1万个券码数量,可以根据项目实际情况自行调整

Array
(
[0] => 478439938353
[1] => 992919492490
[2] => 476600175512
[3] => 512596230643
[4] => 627621923933
[5] => 763100176075
…………
…………
…………
[9998] => 763100176032
[9999] => 797543322210
)

用户端
用Lpop取出指定数量券码,因为Lpop具有原子性,在高并发的场景下,即使同一时间多个用户获取券码,也要按先后顺序一个个执行,这样就保证了每个用户获取出来的券码肯定不会重复的

点击查看代码
/**
 * @从队列中取出指定数量的券码
 * @param $num   券码数量
 * @return array 返回券码数组
 */
function getCode($num){
	$codeArr=[];
	for($i=0;$i<$num;$i++){
		$tmpCode=$redis->LPOP($key);
		if(strlen($tmpCode)==10){
			$codeArr[]=$tmpCode;
		}
	}
	//如果队列中剩余券码数量不足,不足的部分要随机生成
	if(count($codeArr)<$num){
		$diff=$num-count($codeArr);
		for($i=0;$i<$diff;$i++){
			$code=rand(100000000000,999999999999);
			$codeArr[]=$code;
		}
	}
	return $codeArr;
 
}

定时脚本

  • 定时脚本每分钟执行一次,检查队列券码数量,消耗多个券码,就补充多少个券码进去。
  • 随机新生成的券码,要去数据库查下看是否已存在,如果已存在,需重新随机生成一个,再去数据库检查,直至查出有效券号为止
  • Rpush批量插入队列,提高操作效率
    代码示例
点击查看代码
$codeData=$redis->lRange($key,0,-1); //取出券码队列所有数据
$codeData=!is_array($codeData)?[]:$codeData;
$len=count($codeData);
$diff=10000-$len;//差多少个
 
$valid_arr=[];//有效核销码
if($diff>0){
    getValidCode($diff);
    $arr=array_merge([$key],$valid_arr);
    call_user_func_array(array($redis,"rpush"),$arr);//批量插入尾部,提高操作效率
}
 
//递归获取有效券号
function getValidCode($need_num){
    $filter_arr=[];
    for($i=0;$i<10;$i++){
        $code=rand(1000000000,9999999999);
        if(in_array($code,$codeData)){
            continue;
        }
        $filter_arr[]="'".$code."'";
        $valid_arr[]=$code;
    }
    if(count($filter_arr)==0){
        getValidCode($need_num);
        return;
    }
    //查找券码是否已使用
    $used_arr=[];
    $filter_str=implode(",",$filter_arr);
    $sql="select distinct `code` from `user_code` where `code` in ({$filter_str})";
    $tmpData=DB::query($sql);
    foreach($tmpData as $k=>$v){
        $used_arr[]=$v['code'];
    }
    unset($tmpData);
    $valid_arr=array_diff($valid_arr,$used_arr);//减掉数据库中已用的
    if(count($valid_arr)<$need_num){
        getValidCode($need_num);
    }
}

项目盘点
笔者所在企业的票务系统,使用这套券码解决方案,在年数据几千万条的情况下,已经稳定运行多年

posted @ 2024-02-17 20:07  编程教室  阅读(144)  评论(0)    收藏  举报