公司缓存设计:
1. 希望启动后就进行缓存相关的加载,更新等操作,所以定义了一个继承ApplicationRunner的静态类,在其中写了一些启动后刷新的逻辑,同时用scheduleAtFixedRate做一个定时任务,进行相关的定时刷新.还有主动的刷新方法load,供实时数据的刷新使用 2. 数据要求有一定的实时性,故加了一组消息队列,在其他业务对数据产生变更时,发送mq消息,展示端门户收到mq消息后,主动刷新 复制代码
第一次产生问题:
经常有一些缓存的时间存活很长,或者当缓存失效后,直接查询数据库 复制代码
解决方案:
- scheduleAtFixedRate()在做定时任务的时候 是以上一个任务开始的时间计时,120秒过去后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。
- 关于定时线程池,好多人认为设置好频率(比如1Min),它会按照这个间隔按部就班的工作。但是,如果其中一次调度任务卡住的话,不仅这次调度失败,而且整个线程池也会停在这次调度上。
- 所以认为scheduleAtFixedRate()做定时任务,不太稳定,同时当线程池耗尽后,对定时任务也有影响 故换成了@Scheduled做定时任务管理,观察一段时间后,问题暂时解决
第二次产生问题:
业务变更数据后,消息发了,门户也接受到了,但是数据还是历史数据,缓存未刷新 复制代码
解决方案:
- 业务变成的service中有事务,而mq产生的消息是在事务未提交之前发送,门户端接受后就刷新,数据库数据其实还是历史数据. 把mq产生的消息放到了变更的事务之外,同时门户在收到消息后加了20秒的等待时间,到期后刷新缓存
第三次问题:
数据库有更新语句造成数据库报警,但是查询后发现更新频率并不高,同时问题出现在缓存刷新的地方 复制代码
解决方案:
- 第二次将缓存提出来放到了事务之外,但是当某一次事务提交后,产线消息,在消费消息刷新缓存时,有一个更新操作,更新操作和之前的事务操作的是同一张表,造成表锁,等待时长过长,造成报错
具体的业务场景:
- 某一种杂志的网络版整期发布,一个整期中包含多个文章,整期发布除了变更整期的状态,还行处理整期下的文章状态,同时门户的缓存主要针对的是文章,即整期发布后刷新门户的文章缓存
- 整期发布时一个比较大的事务,其中会更新文章相关表的一个状态字段,但是在整期中又循环发送mq消息,去刷新门户的缓存,当门户的缓存刷新的时候,还有同步的一个init文章相关内容的操作,会更新之前事务操作的表,造成事务未提交等待着,而产生的多条消息 同时进行了阻塞 造成了等待超时.
解决办法:
- 采用afterCommet方法, @TransactionalEventListener 监听器,把之前的产生消息的地方换成了事件,当事务提交后,触发监听器事件,产生mq消息
- 把init方法放到其他地方,只刷新缓存,和redis交互,只查 不修改表中的值
下一步考虑使用Canal监听数据库数据变化