开发
考虑问题的角度
背景:在系统中维护了一个本地缓存,在最开始的设计中,考虑到更新操作并不频繁,为了减轻系统不必要的消耗,考虑在redis中维护一个时间戳,每次更新时更新这个时间戳;每次查询时,检查本地缓存的时间戳,并跟redis中的时间戳做比对,不相同的话调rpc接口更新本地缓存;在实际运行中发现,从rpc中并不能获取到实时更新后的数据,导致本地缓存更新的时候并不能拿到最新的数据.
对于本地缓存的更新,有两种方案:
- 定时更新
- 只有存在更新的时候更新,当有多个实例时,需要考虑多实例的同步问题,可以考虑在redis中集中保存一个缓存的时间戳
不同的方案各有优缺点:
- 定时更新 简单粗暴,具有容错性,但更新存在滞后性
- 一次更新 实现更复杂,可以及时把更新同步到本地缓存;但如果更新的途径未做收敛的话会导致其他途径做出的修改无法同步到本地的缓存中
在考虑哪种方案更好的时候,还是要结合业务特性,考究哪种方式导致的问题更易接受.很多时候限制实现的并不是性能问题,而是业务需求.
服务器多实例
背景:系统页面需要展示一些列表信息,这些信息通过rpc接口调外部服务获得.同时通过系统页面可以更新这些列表信息.
为了避免远程调用造成的延时和系统开销,考虑增加本地缓存.在涉及到多实例服务器的情况,正常应该采用分布式缓存,但考虑到列表信息的数据比较大,所以采用了最简单的本地缓存.
实施方案:当更新列表信息时,更新redis中指定key的标识;每次获取列表信息时,先从redis获取标识,如果发现值不为空,则更新本地缓存,同时将标识的值置空,表示本地缓存已更新.
所以问题在于:是否更新的标识只有一个,但是本地缓存却有多个(对应多个服务器实例),所以本地缓存无法按照预期实现更新.
最终的解决方案:本地缓存同时维护一个版本号.当更新列表信息时,redis中的标识值修改为当前的时间戳.每次获取列表信息时,检查本地缓存中是否存在版本号,并且当前的这个版本号是否和redis中的版本号一致,如果不一致的话,更新本地缓存,同时更新本地版本号为redis中的版本号.
对于服务器多实例的情况需要做额外考虑,做分布式控制
其他需要考虑的:
- 对于服务器触发的定时任务,需要统一调度
- 通过服务器去触发一个资源几种的操作(如服务器启动时,初始化redis缓存)