一种临时随机短号的生成算法

1,832 阅读4分钟

背景

短号,即用一个比较短的字符串映射某个比较长的字符串,使得更加易于人们写入。比如一个会议号的唯一ID是一个雪花算法生成的ID,如1077144667185860608,对于用户来说很难手动输入。这时候我们可以生成一个较短的会议ID,比如232 621 942,再把这个短号映射到原来的ID,这样就能够方便用户输入。

在现实中,很多场景并不需要一个永久的短号,也就是说短号是能够复用的。比如上面提到的会议号,会议结束后,即可回收;游戏中的房间号,在一个房间解散后,也可以被回收。因此我们可以通过回收不再使用的短号,从而控制短号的数量。

当然,我们也要求这个短号是随机的,至少是不那么容易被猜到的,否则如果是递增的,那么用户很容易猜到下一个短号,导致一些不愉快的事情发生。

因此,我们需要一种临时的、随机的短号生成算法。

原理

原理很简单,如下步骤:

  1. 随机生成一个短号字符串
  2. 判断是否存在,不存在则存储(也就是SetIfNotExists操作,很多数据库都支持)
  3. 如果第2步成功,则得到一个短号
  4. 否则回到第1步重试

当然,可能会有一些其他的需求,比如某些短号不能被使用,需要限定重试次数等,这些可以根据具体业务进行完善。

碰撞问题

碰撞也就是随机生成的短号已经存在数据库里,设置到数据库失败。如果碰撞概率太大,那么就会导致重试次数太多,影响性能。

这里的碰撞概率其实是和数据库里面的短号数量有关,随着数据库里面的短号越来越多,数据库里短号占短号总数的比例越来越大,那么碰撞的概率也会越来越大。

由于我们的短号是临时的,因此一个短号在使用完之后我们可以释放它,减少数据库里面的短号数量。

清理的方式一般有两种,一种是手动在使用完成之后马上清理,另外一种是定时的清理过期的(或是不再使用)的短号(可以避免手动清理失败导致短号泄露)。

提高短号的位数也可以减少碰撞的概率,因为这样可以降低数据库里短号占短号总数比例。

由于短号是临时的,因此数据库里面的短号数据并不会很多,碰撞的概率也不会特别大。下面是一个简单的测试。

测试

这里我使用Golang实现了算法,测试了不同短号长度下,数据库里不同短号数量的碰撞概率和性能。

碰撞概率

下面的字符集是0123456789,重试次数都是3。

短号长度数据库里短号数量数据库里短号占短号总数的比例%碰撞概率%失败概率%
8100000.10.0500000.000000
810000010.7000000.000000
91000000.10.0500000.000000
9100000010.2100000.000000
1010000000.10.0200000.000000
101000000010.0700000.000000

可以看到,随着位数增加,碰撞概率会下降,在9位的时候,100W的短号其实碰撞概率是很低的,而增加到10位之后,基本上就不会发生碰撞了。

因此,数据库里短号占短号总数的比例最好是在1%以下,这样碰撞概率会很低。

当然,也可以设计更加智能的算法,在发现碰撞概率比较高的时候,可以提升短号位数,减少碰撞。

性能

这里我数据库使用了Redis,测试的的结果如下:

操作QPS
分配短号233612
释放短号274216

这个性能基本上可以满足大部分业务的短号需求。

这个算法的瓶颈基本上在数据库上,因为我们最终需要把短号存储到数据库里。好在一般情况下,我们可以根据短号的值对数据库进行拆分。比如我们的短号是6位数字,那么我们可以这样拆分到10个数据库上,从而提高写能力。

数据库编号存储短号范围
0000000-099999
1100000-199999
2200000-299999
3300000-399999
4400000-499999
5500000-599999
6600000-699999
7700000-799999
8800000-899999
9900000-999999

源码

scode