记一次业务逻辑优化

我们之前新鲜的逻辑是这样的:每个用户在redis中有一个自己的队列,队列中记录的照片的ID,当有新照片产生的时候,不区分性别,往每个人队列最前边插入(lpush)这条数据,每个人的队列最大值为1000,超出部分被截断,当用户看过队列中某张照片后,这张照片会从队列中移除(lrem)

这种模式刚开始没有问题,后来新鲜增加了可以筛选性别的需求。因为队列中所有性别都是混在一起的,所以每次从队列中取出数据后,需要把Photo实体取出来,然后进行性别过滤,把过滤出来的结果再返回给客户端。如果筛选结果后发现数量不足(通常是20),就重新从库中重新查询,拿出前2000张,过滤掉我要的性别(因为我们用的leancloud平台,他们不支持关联查询,我们的photo不记录性别,需要先取出后再通过user才能知道性别),再过滤掉我看过的,能找到就返回,找不到就算了。假如我把筛选改为女,然后我看啊看,看啊看,早晚我会把队列中的女性照片看完。因为我们只查询前2000张,所以后边的照片我根本看不到,除非等着有女性用户新发照片。而且看的照片越多,查询速度越慢。

下边讲一下优化的方法

这个优化是基于假设用户很少切换性别的基础上进行的:

  1. 我为照片表中的数据新增了sex列,为每张照片标记了性别(和发布者性别相同,实为下策略,不过没办法。。。)
  2. 每个用户队列中只保存他所选择性别的照片(全部、男性、女性)
  3. cache中为每个用户保存上次选择的性别 cache:feed:last:sex:u_id,并且记录上次查询到最后那张照片的ID cache:feed:last:photo:p_id
  4. 如果用户再次请求,并且性别不变,那么使用上次查询到的那个照片id继续往后查询m张,因为照片表中有了性别,所以查询效率提升很多。如果中间满足条件的张数大于n,停止查询,并记录最后张ID。
  5. 如果用户切换性别,重新开始查询,更新用户最后一次查询的性别,记录最后查询到的照片ID。从头重新查询。
  6. 在有用户创建新照片以后,需要发通知给offline(redis的发布/订阅实现),之前只发送照片id,现在改为发送照片id和照片性别。
  7. offline收到通知后,将这张照片插入到符合性别和不进行性别过滤的那些用户队列里。

这样做之后性能提升了很多很多~