基于Redis的消息中心缓存搭建
基于Redis的消息中心缓存搭建
背景
问题分析
在以往的经历中出现了在大批用户涌入消息中心时,造成数据库负载急剧升高的问题,经过排查,发现原因主要有以下几点:
- 消息中心相关表中,部分体量较大的数据表没有创建索引,查询操作中数据库连接不能及时释放,导致API服务器不能及时响应,拖垮API服务器;
- 目前的访问量级对消息中心没有做数据库的读写分离,导致在缺失索引的情况下,影响主库性能,拖累其他业务;
目前解决方案
- 结合数据库日志,补全消息中心索引;
- 对消息中心数据库进行读写分离操作,降低主库压力;
仍然存在的问题
在完成上述改造后,仍然有部分较为严重的问题,比如:
- 在发送真对全量用户的布告板消息时,在仅依靠数据库的前提下,采用为每个用户插入相同的消息来处理,从而导致数据库中无效数据的增多。(需求方不允许单独拆分出公告消息,需将公告消息合并至系统消息中)
- 对于一些活动推广消息,增加用户组概念,即:对于推广消息,不用的用户查询到的推广消息的内容不同,依赖数据库进行用户分组的查询,即使在创建索引的情况下,仍会导致查询开销的急剧增长;
- 对于推广消息,支持多图文等富文本格式,依赖数据库查询,在做推广活动时,可能会导致数据库负载明显升高;
需求
类似于淘宝的消息中心,主要体现在如下几点:
- 系统中心首页包涵多个选项卡:喜欢消息,关注消息,评论消息,系统消息(包含公告板消息),推广消息,物流消息等;
- 推广消息分用户组进行查看;
- 推广消息详情支持多图文;
解决方案
基于上述需求单独依赖数据库的话,查询或者更新的开销均较大,因此对消息中心进行缓存改造,由于目前公司的业务需求,缓存系统基于redis,为了增强缓存中内容的可读性,缓存中存放类的信息是采用json格式,其使用的第三方Json为阿里的fastjson;
缓存设计
常规消息
常规消息主要包括:喜欢消息,关注消息,评论消息和物流消息。其主要思想为:
- 每个用户均订阅4个消息概要序列,使用的redis数据类型为Zset(有序集合),采用Zset的原因为:(1)Zset可以进行排重操作,避免对同一个用户出现相同的Id;(2)ZSet中的score值可以用于排序操作,避免用户单个序列中的乱序问题;
- 概要列表中的数据格式为:value: prefix + id,其中没种类型的消息的prefix不同,例如:评论的是”comment_”,关注的为:”follow_”;
- 以上消息的详情存储在一个Map中,其中Map的key为”prefix + id”,value为该消息的详细信息的json串;
- 查询消息的操作流程如下图:
- 新增消息操作:新增消息时候,需要同时更新两个缓存:概要信息和详细信息,按照以上提到的结构进行存储即可
系统及布告板消息
与常规信息不通,布告板消息由运营人员进行相应的配置,主要为消息的展示时间和消息可见的用户组;并且在用户查询系统消息时,需要检测布告板是否有更新,并同步到系统消息列表中,因此在本节中,缓存中需要维护一个针对应用的布告板消息的有序列表,并为每个用户维护一个系统消息的订阅列表;其主要思想如下:
- 缓存中维护一个布告板消息配置的Zset:bulletin_notification_congfig,其中value为bulletin_id_userGroupId,score为display_time
- 缓存中维护一个布告板消息相信信息的Map,其中key为bulletin_id_userGroupId,value为布告板详细信息的json格式;
- 缓存中为每个用户维护一个系统消息的订阅列表,也是Zset格式,其中value为system_id,score为消息产生的timestamp;
- 系统消息的详细内容与常规消息的内容Map一起维护,其key值为”system_id”,value为系统消息的详细内容的json;
- 缓存中维护一个键值对bulletin:update:time,其值为布告板消息更新的timestamp;
- 缓存中为每个用户维护一个键值对bulletin:access:time:{userId},其值为用户上次访问布告板的timestamp;
- 查询消息的操作流程如下:
继续进行中。。。这两天补全。。。。
8. 更新消息的操作流程如下: