生成不重复的邀请码1.如何保证唯一性时间生成?

要求:根据随机策略生成唯一邀请码(字母+数字),不得重复,保证性能。每个用户对应一个邀请码,邀请码必须是唯一的。邀请码需要手动输入,长度不能太长,同时用户无法猜测邀请码的生成逻辑,所以邀请码的生成逻辑也必须是随机的。

目的:生成唯一的邀请码1.如何使其唯一

使用生成的邀请码时间戳生成,并添加随机数,保证不同时间生成的邀请码大部分是不同的(不排序,碰撞概率小)。为了降低碰撞率,可以精确到毫秒粒度,但也增加了邀请码的长度。

缺点:时间长度以秒/毫秒为粒度(10/13位)+随机码(4位)会导致邀请码长度达到10+位,对用户体验极差,容易被猜到. 邀请码的生成逻辑,所以不推荐这种方案。

1.通过Scheme 1的弊端,我们知道要生成一个合格的邀请码首先要解决的问题就是长度。长度的设置与可生成邀请码的数量范围有关。设置过小,会限制邀请码的范围,设置过长的邀请码不利于用户体验。所以考虑到这两种情况,我们可以将邀请码的长度定义为6位。

2.第二个要解决的问题是重复。保证不重复,最好的方式就是简单自增,比如1,2,3,….这样就可以绝对唯一,并且可以绑定用户自增id作为自己- 增加邀请码。当然。这样做的好处是可以保证不重复,但是很容易让用户猜到部分用户的生成逻辑和敏感数据。既然自增可以保证唯一性,那我们不妨换个思路。能不能在自增的基础上打乱规则,保证邀请码的唯一性和不规则性。

实践:一个自增点会导致一个连续且有规律的数字,那么我们可以设置多个池,每个池维护一个自增点,随机去一个池生成一个自增id,可以是随机的。因为邀请码的长度是有限的,所以我们需要计算出邀请码的最大范围值,然后将最大范围值分成几个池子。

长度为6位(数字+字母)的邀请码,根据可用字符数,我们将邀请码的形式设置为36位数字,这样邀请码的取值范围为000000~

zzzzzz,十进制等于2176782335,也就是说我们最多可以生成2176782335个邀请码。如果该编号不符合业务需求,可根据业务需求适当增减。我们假设每个矿池可以存储 10,000 个邀请码,因此将有 217,678 个矿池。我们每个自增积分等于:邀请码=(矿池数*10000+自增积分)转换成32位字符串。

图片[1]-生成不重复的邀请码1.如何保证唯一性时间生成?-唐朝资源网

用一张图来表示上述过程:

注意红色字体,每个矿池都有一个最大自增限制,也就是矿池可以存储的最大邀请数。通过上述过程,可以生成一个没有明显规则的唯一邀请码。当然,如果想尽量不规则,可以去掉前面几个池子,这样就不会出现000001这样的邀请码了。

代码实践(PHP版):主要思路是利用redis集合的特性来存储自增点和池。

<?php
class Code{
protected  Redis $redis;
private $codeIncrementSet = 'code_increment_set';
private $codeAvailableSet = 'code_available_set';

图片[2]-生成不重复的邀请码1.如何保证唯一性时间生成?-唐朝资源网

public function __construct(){ $this->redis = new Redis(); $this->init(); } //初始化 private function init(){ if (0 === $this->redis->exists($this->codeIncrementSet)){ //添加1-217678序号的池子,并且自增点设置为0

图片[3]-生成不重复的邀请码1.如何保证唯一性时间生成?-唐朝资源网

$this->redis->zAdd($this->codeIncrementSet,array_fill(1,217678),0); //保存1-217678序号的可用池子 $this->redis->sAddArray($this->codeAvailableSet,range(1,217678)) } } public function getCode(){ //随机取一个可用的邀请码池子 if (is_null($index = $this->redis->srandmember($codeAvailableSet))){ throw new BusinessException('可用邀请码码数为空');

图片[4]-生成不重复的邀请码1.如何保证唯一性时间生成?-唐朝资源网

} //获取对应需要池子的自增点 $increment = (int)$this->redis->zScore($this->codeIncrementSet,$index) //计算code值 $number = (int)index * 10000 + $increment; //维护自增点 $this->redis->zIncrBy($this->codeIncrementSet,1,$index); //该池子被用完了 if (9999 === $increment){ $this->redis->sRem($this->codeAvailableSet,$index); } //返回36进制的邀请码,用0部位 return str_pad(base_convert($number, 10, 36), 6, '0', STR_PAD_LEFT); } }

© 版权声明
THE END
喜欢就支持一下吧
点赞142 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片