接到需求,设计一群注册用户的好友关系,各自要能查询到自己的好友列表。最早想过用图数据库来进行好友关系存储,但身边没有成熟的案例,网上的资料也比较少。所以还是决定采用传统关系型数据库MySQL来进行设计。
好友关系,如果简单设计成一张表的话,随着注册用户的增多,好友关系势必会呈指数级增加,当系统中用户为10个人时,那么完全添加好友的话,关系数据(假设A和B是好友只有一条数据)则为(9+8+...+1)即55条;当系统中注册用户的数量增长到1000个人时,关系数据最大值为(999+998+...+1)即499500。所以如何设计系统,让用户快速查询到好友关系,是个难点。
这里给出一种思路:根据注册用户的某一个字段(如用户ID),将它的所有好友关系集中存放到一张表中,而不同的用户,会根据这个ID的不同,将它们的好友关系分别散列在不同的数据表中。这样达到了将数据表数据分散,减轻单表压力的目的。但是,按照这个思路设计时,好友的关系必须是双向的,否则A和B的关系,到底是以A的ID进行散列还是B的ID进行散列呢?这样一来,好友的关系数增加了1倍,但是如果散列的足够均衡,这个结果也是可以接受的。市面上很多好友关系在进行设计时,也是采用双向的方式,即A加了B,B同意之后,即建立了双向好友关系,当A删除B的好友关系时,B查询自己的好友列表时,会发现A还在,只有B再删除A,双向的好友关系才会消除。
接下来的工作是如何将这个散列方案实施下来。这里有两个问题,一个是在初期规划时,我们可能并不知道这个社交群体最终的规模,即无法在初期就创建固定数量的关系表,这个表一定是当容量达到一定规模时动态增长的;第二个是,在追求每张表的数据均衡时,我们还要考虑一种情况,即如果初期存入的一批用户被散列到某张表中(即该批用户指向的好友关系存在该表中),而恰好该批用户的好友关系增长速度远远超过其他批次的用户,造成该表数据急速增长的情况,该如何处理?在此背景下,我们来考虑这个被散列计算的字段该怎么设计。
对于以上问题,一个简单的思路如下:假设现在有t_a,t_b,t_c,t_d四张表,都用于存放关系,在它们远没有达到存储规划上限时,我们可以就简单根据现有的各自表的容量,来决定注册用户该字段计算出来该指向哪张表。比如a,b,c现在容量都达到了1000条,而d表的容量才100条,那么在注册用户时,我们就将用户该字段设计成计算结果指向d表。我们还可以设计一个容量因子,在单表数量超过预设上限*该容量因子(如0.7)时,即停止将新用户的字段值设计成计算结果指向该表。另外,如果我们设定一个好友关系上限,如最多500个好友,那么预设的表数据上限除以这个数量限制可以得到这张表最多存放多少个不同的用户,这种做法可以保证这些关系表数量不会异常增长到超过我们预设的上限。
但是,如果我们的系统允许用户无节制的增加好友,那么当用户量不断增长时,上述方案就无法在系统规模不断增长之后还能保证单表关系数量不会突破限制。此时,限制一个用户所有的好友关系在某个单表的方案已经不现实,但我们还是应该尽量这么做,对于单个用户在单表中设置一个关系数量上限,当他的好友关系超过这个数量时,把它存在另外一张表中。那么,如何知道这个用户是否图破了单表存储的设定上限,拥有多张表存储了好友关系,我们可以在注册用户的字段时加一个标记字段,那么查到该用户即可知道这个信息,对于已经突破了单表设定上限的,我们可以再加一张表-突破关系存放表,某用户突破一次,那么在该表中即存一条数据,其中一个字段的值与该用户原先的散列计算值匹配起来可以确定这次突破后存放的新表名称。这样既可快速定位到这些新表的名称,从而查询出所有的好友关系,另外,好友关系表中对单表单用户的好友关系数量做出了限制,所以当有N个表存放了该用户的数据时,那么好友的数量即,(N-1)*固定容量+第N张表的好友数量,所以统计好友总数也不复杂,只需要知道第N张表的数量即可推算出总数。
暂时想到这么多,欢迎评论留言补充思路或者其它拓展点。