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

247 阅读2分钟

需求描述

在开发优惠券系统或票务系统的时候,经常要生成纯数字码,券码要求: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);
    }
}

项目盘点

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