用户ID生成唯一邀请码的几种方法

您所在的位置:网站首页 优酷号入驻为什么要邀请码 用户ID生成唯一邀请码的几种方法

用户ID生成唯一邀请码的几种方法

2024-07-06 04:14| 来源: 网络整理| 查看: 265

在这里插入图片描述在这里插入图片描述文章目录1.需求描述2.需求分析3.字符集4.方法一:随机数+唯一性判断(不可逆)5.方法二:Hash+唯一性判断(不可逆)6.方法三:进制法(可逆)7.方法四:进制法+扩散、混淆(可逆)8.小结参考文献1.需求描述

有一个业务需求,需要根据用户 ID(数值型 >=10000000)生成一个唯一的长 6 个字符的邀请码,用于邀请新用户注册。

2.需求分析

从业务需求和一般产品邀请码的使用体验上来看,邀请码有以下几个特点:

不可重复:不用用户 ID 生成的邀请码是不同的;唯一确定:一个用户 ID 只能生成一个邀请码;是否可逆:是否需要通过邀请码反推对应的用户 ID 是什么。

本文将以 Golang 为例,给出根据用户 ID 生成唯一且不重复的邀请码的常见方法与实现示例。

3.字符集

首先需要确定组成邀请码的字符集,一般采用数字和英文大小写字母共计 62 个字符。如果长度时 6 的邀请码,那么空间大小 62^6 = 56,800,235,584,这是一个非常大的空间,足够用户量为亿级别的业务使用。

代码语言:javascript复制// AlphanumericSet 字母数字集 var AlphanumericSet = []rune{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', }

当然,为什么提升用户体验,可以把 O、o、0、I、1 这五个形似易混淆的字符去掉,本文就不去了。

4.方法一:随机数+唯一性判断(不可逆)

使用用户 ID 作为种子初始化随机数发生器,随机生成字符集下标,取出对应的字符拼接成邀请码。

注意,这里会存在生日问题,随着已生成的邀请码数量的上升,发生碰撞的概率将会大大增加。

如果生成 100W 个邀请码,假设前 100W 一个都不重复,那么下一个重复的概率是((1/62)^6 * 100W)≈1/5.6W,冲突率已经到了在万分之一的概率,远大于直觉(1/62)^6。

代码语言:javascript复制// GetInvCodeByUID 获取指定长度的邀请码 func GetInvCodeByUID(uid uint64, l int) string { r := rand.New(rand.NewSource(int64(uid))) var code []rune for i := 0; i < l; i++ { idx := r.Intn(len(AlphanumericSet)) code = append(code, AlphanumericSet[idx]) } return string(code) } fmt.Println(GetInvCodeByUID(100000000, 6)) // uGkK9K fmt.Println(GetInvCodeByUID(100000001, 6)) // UPFlHa fmt.Println(GetInvCodeByUID(100000002, 6)) // keKZ01

缺点:

唯一性判断引入第三方组件,增加依赖,降低了可靠性;在 >=99.99% 的情况下,唯一性判断都是通过的,浪费存储资源。5.方法二:Hash+唯一性判断(不可逆)

对用户 ID 做 Hash(如 MD5)运算,获取散列值后取散列值的多个字节映射到字符集,然后组成邀请码。因为可能发生碰撞,所以需要做唯一性判断,比如借助 DB(Redis)来判断。

代码语言:javascript复制// GetInvCodeByUID 获取指定长度的邀请码 func GetInvCodeByUID(uid string, l int) string { // 因为 md5 值为 16 字节 if l > 16 { return "" } sum := md5.Sum([]byte(uid)) var code []rune for i := 0; i < l; i++ { idx := sum[i] % byte(len(AlphanumericSet)) code = append(code, AlphanumericSet[idx]) } return string(code) } // 示例 fmt.Println(GetInvCodeByUID("100000000", 6)) // btCcwX fmt.Println(GetInvCodeByUID("100000001", 6)) // sxQ1mq fmt.Println(GetInvCodeByUID("100000002", 6)) // swA3pk

缺点:

唯一性判断引入第三方组件,增加依赖,降低了可靠性;在 >=99.99% 的情况下,唯一性判断都是通过的,浪费存储资源。

我们可以写一个单测来看下长度为 6 的邀请码,在字符空间为 62 ,一千万用户量下的碰撞率。

代码语言:javascript复制func TestGetInvCodeByUID(t *testing.T) { sUID, eUID := 10000000, 20000000 var md5ConCnt int // md5 前 6 字节冲突次数 var codeConCnt int // 邀请码冲突次数 mHash := make(map[uint64]struct{}) mCode := make(map[string]struct{}) // 统计 1KW 个用户ID生成邀请码发生碰撞的次数 t.Run("getConflictNumTestCase", func(t *testing.T) { for uid := sUID; uid < eUID; uid++ { sum := md5.Sum([]byte(strconv.Itoa(uid))) md5Value := uint64(sum[5]) | uint64(sum[4])


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3