需求:按照随机策略生成一个不重复的邀请码(字母+数字),既要不重复又要保证性能。每个用户对应一个邀请码,必须做到唯一性。邀请码的需要手动输入所以长度不能太长,同时不能让用户猜到邀请码的生成逻辑,所以邀请的生成逻辑也必须要随机。

目的 :生成不重复的邀请码

1.如何做到不重复

  • 方案一:

利用生成邀请码时间戳进行生成,并且加上随机数,能保证不同时间生成的邀请码大部分不同(不排序小概率碰撞)。为了减少碰撞率,可以精确到毫秒颗粒度,但同时也增长了邀请码的长度。

弊端:秒/毫秒颗粒度的时间长度(10/13位)+ 随机码(4位)会导致邀请码的长度达到10+位,这对于用户的体验是极其不好的,而且也容易被猜到邀请码的生成逻辑,因此此方案不推荐。

  • 方案二:

1.通过方案一的弊端,我们知道生成一个合格的邀请码我们需要解决的第一个问题就是长度。长度的设置是关乎可生成邀请码的数量的范围,设置太少的话邀请码的范围有限,设置太长的码邀请码对于用户的体验不好。所以考虑这两个情况,我们可以将邀请码的长度定义6位。


2.要解决的第二个问题就是重复的问题。要保证不重复,简单来说最好的方式就是自增,例如1,2,3,....这样子就能保证绝对唯一,可以将用户的自增id作为邀请码的自增值做绑定。这样做的好处就是能够保证不重复,但是很容易被用户猜测到生成逻辑以及一些用户的敏感数据。既然自增能够保证唯一性,那我们不妨换一种思维去思考,可不可以在自增的基础上面将规律给打乱,这样就能保证邀请码唯一且无规律了。


做法:一个自增点会导致数量连续有规律,那么我们可以设置多个池子,每个池子维护一个自增点,随机去一个池子通过自增id生成,就能够做到随机了。因为邀请码的长度有限制,所以我们需要算出来邀请码最大的范围值有多少,然后将最大的范围值分成若干个池子。

6位(数字+字母)长度的邀请码,根据可取字符的数量,我们将邀请码的形式设置为36进制数,这样邀请码的范围值就是为000000~
zzzzzz,也就是等于十进制的2176782335,相当于我们最多能够生成2176782335个邀请码。如果此数量不满足业务上的需求,可以根据业务要求适当增删。我们假设每个池子能够存储邀请码的数量为10000个,则会有217678个池子。我们每个自增点就等于:邀请码 = (池子number * 10000 + 自增点)转成32进制 的字符串。

用个图来表示以上的过程:

要注意红色字体,每个池子有最大自增点限制,为该池子能够存储邀请的最大数量。通过上述过程就能做到生成一个唯一且无明显规律的邀请码。当然要想最大程度无规律的话,可以将前几个池子给剔除掉,这样子就不会出现000001这样子的邀请码


代码实践(PHP版本):主要思路是利用redis集合的特点进行存储自增点以及池子。

<?php
class Code{
protected  Redis $redis;
private $codeIncrementSet = 'code_increment_set';
private $codeAvailableSet = 'code_available_set';
public function __construct(){
    $this->redis = new Redis();
    $this->init();
}

//初始化
private function init(){
   if (0 === $this->redis->exists($this->codeIncrementSet)){
    //添加1-217678序号的池子,并且自增点设置为0
    $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('可用邀请码码数为空');
    }
    //获取对应需要池子的自增点
    $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);
}
}

以上就是整体代码的实现,具体的一些细节可根据业务自定义调整。

2.如何做到高性能

生成用户邀请码是一个频繁的操作,如果每次实时生成的话性能会比较低。可用利用空间换时间的思维。预先生成一批邀请码然后在需要的时候直接拿出来用即可。这种思路在开发中很常见,异步处理,用空间换取时间。通过这种方式就能快速的生成邀请码。


以上就是如何生成唯一无规律邀请码的策略,方法很简单却很有效。将思路输出成文章的形式,能够更好地加深理解,以及能够让更多的人看到,指出其中的不足。

互勉:保持空杯心态,做一个有态度的程序员。

个人原创,转载需取得作者同意。个人博客地址:https://www.goldenleek.top

内容来源于网络如有侵权请私信删除

文章来源: 博客园

原文链接: https://www.cnblogs.com/leeee/p/16386541.html

你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!