2 条评论
06 Jan 2013

基于Redis的Feed推送系统(二) - 动态聚合器

动态聚合的部分全部基于redis的sorted set, 采用mapreduce计算. 这就是前文中的Aggregator, 主要用于初始化和rebuild.

每个用户都有个人feed的队列, 用户得到的动态就是把所有关注者的feed队列内容聚合起来. 这里采用了mapreduce的计算方式, 先取出每个人的Top 20, 得到N个关注者的20xN条feed, 再进行排序得到最终的Top 20.

对性能做测试的时候, 按照关注上限3000人, 每人队列10000条做了模拟. 循环取3000次15s, 改用pipeline后0.5s, 最后变态地使用了邪恶的eval, 用lua脚本一次性在服务端做完再返回, 结果是0.2s.

    
do
    local ret = {}
    local t = loadstring('return ' .. ARGV[1])()
    for k,v in pairs(t) do
        local key = 'u:' .. v .. ':photos'
        local tmp = redis.call('ZREVRANGEBYSCORE', key, '(' .. ARGV[2], '-inf', 'WITHSCORES', 'LIMIT', '0', ARGV[3])
        table.insert(ret, tmp)
    end
    return ret
end

悲催的是redis的单线程的, 所以这个操作的并发性能很差. 根据redis文档描述 Time complexity: O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. 耗时主要取决于每页的feed条数和关注的人数.

当然可以通过多个read slave来提高并发, 也可以将个人feed队列分布存储在多个redis中, 通过二次mapreduce并行计算.


需要翻页时提供上一页最后一条的score, 取小于score的20条, 才可以保证mapreduce翻页结果正确. 实际中简单地使用了精度到毫秒的时间戳作为score.



intval((microtime(true) - $epoch_ts) * 1000)

 



已有 2 条评论

  1. 1

    这是拉模式, 我觉的还是用推模式做简单.

    3000个粉丝, 发feed时分别推到这3000个人的feed列表即可.

    1. alswl

      拉必须做,推是可选的。否则无法处理大 V 帐号

添加新评论