总结一个实际应用场景中的缓存架构,该缓存的结构由Redis缓存和内存缓存两级组成,内存缓存部分为空时,请求过来之后自动去Redis缓存获取并缓存到内存,设置过期时长;Redis缓存由单独的后台服务维护,当数据表中有增删改出现时,创建对应的MQ,后台服务通过接受MQ,来更新对应的Redis缓存数据。后台服务添加数据一致性验证线程,来保证MySQL数据与Redis数据最终一致。
MySQL部分
MySQL中需要缓存的数据表基本内容如下:
其中表中已经添加了depart_id,point_id的联合索引。
Redis 及内存缓存中也是以{[depart_id]|[point_id]}为key做缓存。
接口部分
接口请求参数depart_id,point_id为必填,user_id,event_type为非必填。
接口逻辑
接口请求过来的时候,先去内存缓存查询,查询不到对应key,接口程序自动去Redis中获取,如果获取不到,表示数据不存在(Redis中的数据,默认跟MySQL中一致,这里可能会有一定的延迟,但是基本是最终一致的)。
页面部分
页面修改过程,修改内容更新MySQL相应数据,添加MQ消息,用以告知后台服务有数据被修改,MQ消息体内包含被修改的数据的key,和数据id等信息。
Redis数据维护服务部分
我在设计这个服务的时候,考虑到可能会有数据处理的时候数据丢失的问题,把服务分为了两部分,简单说,服务中有两个线程,一个线程对接MQ,去作为MQ的消费段,处理MQ中更改的数据,MQ中记录了缓存所属的MySQL表及需要更新的缓存的
MQ中记录了被修改的数据的id,及所属的Redis Key,及一些其他辅助内容。
1. MQ消息消费线程(根据MQ触发来更新Redis缓存) Redis数据维护服务读取到MQ之后,按照id读取到相对应的MySQL数据行,根据Redis key读取到Redis缓存中对应的数据集合(我这里设计的Redis缓存一个KEY对应的是多条数据,因为除了Key之外,接口查询的时候还有其他非必填字段作为筛选项),根据集合中的原MySQL 表id,对集合中给集合中对应的数据进行更新,然后将更新之后的新集合刷新到Redis中。
2.数据监护线程,后台每隔一段时间,轮询数据库中所有的depart_id+point_id的组合,分别获取对应的数据,与Redis中对应的key的数据比较,如果发现有不一致的数据,则创建MQ消息(同页面更新时一样),这里边有可能出现,在此线程轮询到某个Redis Key的时候,页面恰好刚刚更新了数据,并且MQ消费线程还没有将MySQL的修改更新到Redis中的情况,我这里没有处理,也就是会出现重复的更新,因为我的监护线程一天只执行一次,出现重复更新的情况也不会特别多。但是,如果监护线程间隔时间较短的话,可以在Redis中维护一个set集合,用来存储MySQL有更新,但是Redis中未来的及更新的key,这样在监护线程扫描到有key未更新时可以跟set结合中的key做比较,避免重复更新。