由注册用户名而引发的一些思考

假设当前的场景是这样的,用户量会很大,有一个注册接口,用户在注册时会输入一系列信息,比如用户名(主要想表示它为冷数据)等等。要求:1.能承受一定的并发访问。2.即使是并发调用,要必须要保证用户名不能重复。3.单次注册耗时尽可能短。只考虑单个mysql,单个redis。

 

第一个想到的是,为了使得系统承受一定的并发,那么需要在注册接口进行限流,而限流算法就不再这里赘述了。

那么,可能有这么些解决方案:

1. 利用redis set结构来进行存在性判断。优点:速度快,无需进行额外的并发控制。缺点:由于认为用户名为冷数据,浪费了大量内存。

2. 利用redis boolFilter结构来进行存在性判断。优点:速度快,无需进行额外的并发控制,占用内存较小。缺点:由于用户量很大,如果只使用较小的内存,那么很可能会存在很多误判。

3. 直接使用mysql。优点:速度较快,无需进行额外的并发控制,不占用内存。

 

现在来考虑如何来mysql这一层,用户名一般存在于用户表中,而由于用户量会很大,那么必定涉及分表,但一般用户表的水平拆分依靠的变量为用户id,比如分为10张,那么用户id%10,方便查询用户id时可以锁定到某张表,或者某个区间。那么这跟用户名是没关系的,也就是说,当我们要判断一个用户是否存在,依然要遍历所有表才能得到结果,而此时,假设我们有在用户表中,对用户名(对应Java String类型)建立了索引,它依然是很耗时的。

 

再次确认需求,存在性判断。

 

同样是分表,那能不能依靠用户名来分呢?原来的依靠用户id来分也确实是有它的需要的,那么我们可以把用户名做成一张大表(将它冗余出来),现在对这张用户名大表来拆分。那么怎样分才能使得我们在得到一个用户名时可以锁定到某张或者某个区间呢?这里不难想到使用hash,也就是每个用户名都会对应着一个hash值,参照用户表的拆分,我们也可以简单地hash值%10,当然也可以使用别的策略。总之,现在当用户注册时,输入用户名,可以对它进行计算得到一个hash值,最后锁定到某张表,只需遍历那张表就可以知道答案,从速度上来说,这相比之前要遍历所有表要快很多。

 

再考虑单张表遍历的优化。我们可以再利用一下hash值,假设这个hash值的字节数大小为8,我们在用户名表上,增加一列字段名为hash,意为用户名的hash值,我们对其建立索引,那么我们在判断时,遍历的就是索引key为bigint的 B+树,数字的比较肯定要比字符串的比较快。当然,同样可能会存在误判,这可以依据用户量适当调整hash值字节数大小,理论上,字节数越大,hash冲突越低。

 

再次确认需求,存在性判断。

 

set结构可以O(1)时间判断存在。当前我们是O(logn)地遍历B+树,等值判断,那么自然就想到了hash索引。遗憾的是,在innodb引擎下,我们作为用户,是无法建立hash索引。所以,这里可能会想到使用别的引擎。但其实,innodb在优化的时候,会建立自适应hash索引,所以,我们可以认为最后也是会建立hash索引。那既然会建立hash索引,那是不是用户名表就用不着这个hash值,直接对用户名这个字符串建立索引就好了,反正最后会被优化成hash索引。但优化总是不稳定的,我觉得还是建立的好。(唯一索引,这样就不会重复了)

 

需要说明的是,以上还没有经过验证,只是分享一下这些想法!

posted @ 2020-09-19 22:18  Allen没有青春  阅读(158)  评论(0编辑  收藏  举报