Redis使用zunionstore消耗大量内存

几个月前上线了一个服务,有一个业务需求是要求输出用户订阅的话题(tag)下的文章列表,关注的作者(author) 发布的文章列表,这两个列表取并集按更新时间逆序输出。

于是自然而然想到了使用redis的zunionstore方法。先给每个话题创建一个有序集tag:{tid}:posts,用来存放相关联的文章列表

1
2
3
4
5
6
7
ZADD tag:1:posts 1495091379 1001
ZADD tag:1:posts 1495091379 1002
ZADD tag:1:posts 1495091379 1003

ZADD tag:2:posts 1495091379 1002
ZADD tag:2:posts 1495091379 1003
ZADD tag:2:posts 1495091379 1004

然后给每个作者创建一个有序集user:{uid}:posts,用来存放他发布的文章列表

1
2
3
4
5
6
7
ZADD user:1:posts 1495091379 1002
ZADD user:1:posts 1495091379 1004
ZADD user:1:posts 1495091379 1006

ZADD user:2:posts 1495091379 1001
ZADD user:2:posts 1495091379 1003
ZADD user:2:posts 1495091379 1005

假设用户a关注的话题1和作者1,则用户a的订阅列表中会输出话题1和作者1的文章列表的并集,于是使用zunionstore求并集

1
ZUNIONSTORE user_feeds:a: 2 tag:1:posts user:1:posts

刚开始使用时很完美,但是后来阿里云发出警报redis占用的内存从1G增长到了10G,于是开始尝试分析redis的数据,通过简单的redis_cli info发现如图

然后使用

1
./redis-audit.rb -d 0

通过redis-audit的采样分析发现user_feeds:xxx的keys占用99%的空间,但是分析发现user_feeds:xxx的keys一共才占了所有key总数的1%。于是尝试删除user_feeds:xxx的key

1
redis-cli keys "user_feeds:*" | xargs redis-cli del

内存瞬间下降到400M

后来发现有很多用户关注的话题和作者数量都在1k左右,也就是说zunionstore需要求1k左右文章列表的并集,因为业务代码中zunionstore会每次请求时都执行,所以在找到完美求并集的的解决方案之前,只能写了一个脚本监测redis的内存开销,一旦大于5G,则自动执行一次删除user_feeds:* keys的命令。如果有对redis比较熟悉的童鞋,还望不吝赐教!