问题:
某海量用户网站,用户拥有积分,积分可能会在使用过程中随时更新。现在要为该网站设计一种算法,在每次用户登录时
显示其当前积分排名。用户最大规模为2亿;积分为非负整数,且小于100万。
存储结构
首先,我们用一张用户积分表user_score来保存用户的积分信息。
表结构:
Field | Type | Null | Key | Default |
uid score | int(11) int(11) | NO NO | PRI | 0 0 |
示例数据:
uid | score |
1 | 232 |
2 | 100289 |
3 | 0 |
4 | 99 |
5 | 878 |
6 | 99999 |
7 | 298892 |
8 | 69891 |
9 | 8 |
10 | 555 |
下面的算法会基于这个基本的表结构来进行。
算法1:简单的SQL查询
首先,我们很容易想到用一条简单的SQL语句查询出积分大于该用户积分的用户数量:
select 1+count(t2.uid)as rank from user_score t1,user_score t2 where t1.uid=@uid and t2.score>t1.score
算法特点
优点:简单,利用了SQL的功能,不需要复杂的查询逻辑,也不引入额外的存储结构,对小规模或性能要求不高的应用不失
为一种良好的解决方案。缺点:需要对user_score表进行全表扫描,还需要考虑到查询的同时若有积分更新会对表造成锁定,
在海量数据规模和高并发的应用中,这样做性能是无法接受的。
算法2:均匀分区设计
在许多应用中缓存是解决性能问题的重要途径,我们自然会想:能不能把用户排名用Memcached缓存下来呢?不过再一想
发现缓存似乎帮不上什么忙,因为用户排名是一个全局性的统计指标,而并非用户的私有属性,其他用户的积分变化可能会
马上影响到本用户的排名。然而,真实的应用中积分的变化其实也是有一定规律的,通常一个用户的积分不会突然暴增暴减,
一般用户总是要在低分区混迹很长一段时间才会慢慢升入高分区,也就是说用户积分的分布总体说来是有区段的,我们进一
步注意到高分区用户积分的细微变化其实对低分段用户的排名影响不大。于是,我们可以想到按积分区段进行统计的方法,
引入一张分区积分表score_range:
表结构:
Field | Type | Null | Key | Default |
from_score | int(11) | NO | PRI | 0 |
to_score | int(11) | NO | PRI | 0 |
count | int(11) | NO | 0 |
from_score | to_score | count |
0 | 1000 | 48892 |
1000 | 2000 | 25329 |
2000 | 3000 | 12568 |
3000 | 4000 | 10207 |