为了提升系统的响应速度,通常会系统中使用缓存,例如:中央缓存,本地缓存等。但是使用缓存有好有坏,坏处是,如果缓存数据是旧的,那么应用输出的数据便是错误的数据,可能造成严重的影响。
本文介绍一些让缓存保持新鲜以及使用缓存的一些技巧。
一旦db数据发生变化,则可以发送一条mq消息,通知消费者,信息有变动,消费者感知到后,立刻调用刷新缓存的接口,把数据涮新到中央缓存里。这里有三个点要注意一下:
1、如果缓存中的数据存在依赖关系,那么必须级联的进行刷新,不然会出现,数据在缓存中不一致。
2、MQ可能挂掉,得使用其他辅助方案来刷新中央缓存。比如说,定时任务全量刷,增量刷。
3、如果应用中Mysql使用一主多从,那么一定要注意主从延迟的问题,如下图:
一旦消息消费者判断数据已经从master库同步到slave1的时候,就开始调用调用刷缓存的接口,刷新缓存的应用这个时候读取的是slave2,那么可能数据还未从master同步到slave2,那么这个时候读取的数据其实还是旧的,刷到缓存的数据也是旧的。
基于这种情况,有一种办法,就是保证消息消费者和刷新缓存的应用读取的db必须是同一个。这样即使出现主从延迟,也没问题。
备注:一主一从不会出现这种情况。
可以使用一个job,每天凌晨把所有
刷新到中央缓存里。
全量刷,有个缺点,就是得等到晚上凌晨,数据才能被刷到中央缓存里。如果想让数据快点刷到中央缓存里,可以使用一个增量的job,每隔两个小时,把最新的热点数据刷到缓存里。使用增量job也注意一个问题,避免高峰期刷。
比如说,你的应用是早上十点,晚上八点流量最大,那么这个时候,就不要去刷缓存了,给应用多留一些资源,应付大流量。
有些情况下,中央缓存也会缓存外部其他系统的数据,如果这些数据访问比较频繁,那么可以使用
哨兵机制,当这些数据快要过期的时候,主动调用外部系统,把数据刷新到中央缓存里。个人认为这是一个相当不错的技巧。因为调用一次外部系统的接口,开销还是很大的,而且也需要时间,如果能提前调用的话,那么将大大提高本系统接口的响应速度。
使用本地缓存的时候,有几点要注意
1、本地缓存的选型,到底是使用堆外缓存还是堆内缓存。
堆外缓存有:
OHC、ehcache、OpenHFT Chronicle Map 2、OpenHFT Chronicle Map 3。
堆内的有:ConcurrentHashMap、Google Loading Cache
缓存的数据量比较大的话,使用堆外的,量小的,使用堆内的。
2、注意同一个key的防穿透,就是说,有相同的key并发的访问到本地缓存,那么应该只是让其中一个key穿透过去,其他的key只能等待,防止大量的key穿透过去,对中央缓存或者db造成压力。
1、自动过期。这种方式非常的简单,就是设置本地缓存数据的失效时间,比如说,设置5分钟,5分钟后自动失效,请求穿透过去。
如果使用数据过期这种方式的话,要注意,当系统有大流量的时候,
。
2、发布订阅的方式,在每个应用中部署一个监听者,写数据的一方,一旦把数据写到db后,就通知每个应用的监听者,刷新本地缓存。这种方式稍微有点难度,也不是特别可控。目前我参与的应用,本地缓存数据的刷新策略是自动失效。
为了能知道缓存的命中率,分析缓存框架的效果,最好用程序输出缓存的命中率。如果缓存命中不高的话,就得分析原因了。可能是算法有问题,缓存key的维度有问题等等。