关于QQ号的分发管理机制的基本方案的设计猜想和分析讨论

来源:互联网 发布:labview读取excel数据 编辑:程序博客网 时间:2024/05/22 03:02

关于QQ号的分发管理机制的基本方案的设计猜想和分析讨论

众所周知,QQ号最开始是5位,后来逐渐变为11位,我开始用QQ的时候,一般都是申请到10位,有时候能申请到9位,据说还有传闻“同学的同学”、“朋友的表哥”这些人申请到7位号码,那时候中小学生,去到网吧都会注册几次,看能否申请到短号。言归正传,这也六七年过去了,QQ的用户数量不断增加,受到微信冲击之前,增长速度应该也是在上升,能停留在11位,一个不可忽略的因素就是号码回收机制。QQ的管理系统会自动回收不活跃帐号。具体来说,新注册的号码,一周内不登陆就会被冻结,登陆了几次之后,这个周期会变长一些,随着级别的增高,被冻结的可能性也会阶段性地降低。

我今天脑海里突然想到这个问题:QQ号码的数据库(表)要怎么设计?

 

对于这个问题,我在网上搜了一下。已有的回答或讨论,无外乎这几种:

  • 号码字段自增
  • 使用UUID
  • 预先生成

对于第一种(号码字段自增),方案确实可行,反对的意见只有“不想用这种方式”,但是用什么类型存储,可以表示多大的范围,没有人细致分析,也没有考虑提及。

使用UUID,能够保证分表、分库的唯一标识,但是位数太多(32位),且字母数字大小写混合,无法记忆,更别说作为QQ号码了,没有考虑实际应用场景。

预先生成的方案,后生成的号码,可能与先前生成的号码有重复,这样一来,随着已分发的号码数的增多,后期生成的号码中,可用的越来越少,分发的成功率越来越低。

因为没有找到满意的方案,或者有利弊分析的回答,所以我在几个朋友中提出了这个问题,听听他们的意见。

我的问题是:QQ号的注册机制大概是什么样的?

问题描述:QQ的分发,号码长度从以前的5位开始,到现在的11位,同时还会将回收来的一些短号,进行重新分发,你们觉得新号的生成和回收号的重新分发,是怎么进行的?数据表该如何设计?

 

得到的第一个回答是:

检测这个账号超过一定时间(比如3年)没用,就给他注销,注销的账号存进一个新的表中或者容器中,账号类型比如设计varchar长度为20位。

方法一:我随机生成的20位随机数,截取前11位,如果可以插进去数据库表中,那一定是可用的,否则再试,这样有点难道估计腾讯不会这样做;

方法二:拿11位的来说就可以从小到大按顺序分配,后9位存进数据库完全可以用空格替代,读数据时候再截取前11位,如果再生成新的账号的之前检查在此之前有没有存注销账号的表中有没有数据,有的话就使用,将其列入正在使用的表中,同时把它从注销的列表中删除。

数据库的(基本)表有:“正在使用的”、“已注销的”这两个。

 

他的回答,没有说明,无论是20位还是11位,数据怎么生成?是每次随机生成1位,随机20次?或者是一次得到20位?后者的变量用什么数据类型?另外我质疑了“方法一”采用“先生成号码,后尝试存储”的方式,在注册用户数很多的情况下,每次注册可能要尝试生成大量的号码,进行许多次数据库插入操作,存储成功率就会逐渐降低。还有,char类型实现“从小到大按顺序分配”不好。

 

过了一会儿,第二个回答:

这个场景就是在分布式环境下唯一ID 的生成,要保证ID的唯一性,就需要一把可靠的分布式锁,这种锁的实现 基本上有三种,用数据库、用缓存实现、用Zookeeper实现,三种方案的比较:

从理解的难易程度角度(从低到高)——

数据库 > 缓存> Zookeeper

 

从实现的复杂性角度(从低到高)——

Zookeeper >= 缓存 > 数据库

 

从性能角度(从高到低)——

缓存 > Zookeeper >= 数据库

 

从可靠性角度(从高到低)——

Zookeeper > 缓存 > 数据库

 

有了可靠的锁以后,那怎么生成就容易了,而且,我可以预先 生成一堆可用的唯一ID,注册的时候,直接从里面拿就行了

 

他的回答,主要是考虑分布式的数据完整性和唯一性问题,但是对于我的问题——单独一个数据库的基本设计,没有细致的回答。他现在第一梯队的互联网公司开发,果然考虑问题首先会想分布式、负载均衡、高并发。我还是质疑了他的“预先生成”不能保证唯一性,问了他如何避免这个问题。顺带联系第一个回答的“方法二”评论了一下——

我的一个想法是:

将QQ号码这个字段设置为自增,用BIGINT类型存储。BIGINT占8字节,无符号可表示范围0~18446744073709551615(2^64 -1),也就是说,最长能表示20位。

实际情况肯定不能这么长,因为我们能轻松记住的数字长读是很短的。另外,有帐号回收机制的存在,所以,帐号的实际需求量是在十亿数量级以内的,从这个角度来说的话,用int存储也差不多能行(4字节,无符号,可表示范围:0~4294967295)

另外,用数值类型的存储,可以比char类型更快比较大小,便于"按序分配"

不过,自增字段存在一个问题:不能给该字段赋值。所以,delete某个QQ号码之后,就不能再回收了。也就是说,不能采用“删除之后,添加到'已注销'表,重新分发的时候再拿出来放回'正在使用'表 ”的方式。

这个问题也可以避免:采用标记删除,而不是物理删除。

比如,可以设置两个字段,一个用于标记删除,一个用于标记冻结。正常用户,这两个字段都是非标记状态。这样,一个号码在长时间未登录后,被系统对其进行回收,先标记为冻结状态。冻结一段时间之后,标记为删除状态。在用户申请号码的时候,系统为其分发号码之前,先从数据表中筛选出被标记删除的号码,分配给申请者,然后执行update操作,将该申请者的信息更新到这个号码。

区分“冻结”和“删除”状态,有个好处:在冻结期内,用户尝试登录,系统能够给一些提示(比如告诉用户,该号码已被回收),让用户知道自己“已经失去了这个号码”。

当然,不需要用两个字段来标记两种状态,用数值区分两个状态就可以。

 

第二个回答的朋友,在临近下班的时候,对我的想法做了分析和评论

哇塞,考虑比较全面阿!ID 自增的话,其实,单表数据量超过千万的话,就基本 hold 不住了,所以就需要 分表、分库,但是要保证这个时候的ID唯一性,那就搞 id 区间隔离,比如:两台mysql——A和B,A只生成奇数,B只生成偶数 等等,然后备库,再实时同步,做好容灾。但是实际情况下可能会遇到各种问题,所以也不一定就是最好的方案。

 

下班不久,又一个同样级别的朋友说了一下他的方案:

可以自己去创建一个账号池,池内分为两个子池,一个是已使用,一个是未使用,当未使用池中剩余账号剩余数量低于一定的阀值时即可向未使用的池中注入新的账号,注入时使用数字自增即可,存储设备可以使用缓存。相比较数据库更加减少了额外磁盘空间的开销,而且在读取熟读上缓存要比数据库快很多。而且可以为每个账号设置生命周期,每次登入时可以刷新这个账号的生命周期,如果在生命周期规定的时间到了,依旧没有登入话这个数据将会被缓存自动删除,这里可以增加一个触发机制,就是在删除的同时将账号注入到“未使用”的池子中,这样,这个账号即可以再次被别人所注册。借助分布式锁,可以去读取“未使用”的池,从而获取一个未被使用的账号,从而进一步注册,将注册后的账号及用户信息在存到数据库。至于暂难恢复及高可用方面,可以使用些分布式缓存技术,多点部署,这样高可用及暂难恢复均有了保障

 

由于我自己并没有使用过数据库连接池,对于数据库连接池的技术细节,不好评论,这些方案猜想和分析讨论暂时贴出来,等以后再次回想到这个问题,或许有新的想法,到时再提。

0 0