两种缓存:
- 本地缓存(HashMap/ConcurrentHashMap、Ehcache、Guava Cache等)
- 缓存服务(Redis/Memcache等)
什么情况适合用缓存?
- 短时间内相同数据重复查询多次且数据更新不频繁,可以选择先从缓存查询,查询不到再从数据库加载并回设到缓存的方式,此种场景较适合用单机缓存;
- 高并发查询热点数据,后端数据库不堪重负,可以用缓存来扛;
缓存选型:
- 如果数据量小,并且不会频繁地增长又清空(这会导致频繁地垃圾回收),那么可以选择本地缓存。如果需要一些策略的支持(比如缓存满的逐出策略),可以考虑Ehcache;如不需要,可以考虑HashMap;如需要考虑多线程并发的场景,可以考虑ConcurentHashMap;
- 其他情况,可以考虑缓存服务;
什么时候更新缓存?如何保证更新的可靠性和实时性?
更新缓存的策略,需要具体问题具体分析。例如,目前约10万个商品数据采用了redis作为缓存服务,具体更新的策略有两个:
- 接收数据变更的消息,准实时更新;
- 给每一个缓存数据设置5分钟的过期时间,过期后从DB加载再回设到DB。这个策略是对第一个策略的有力补充,解决了手动变更DB不发消息、接消息更新程序临时出错等问题导致的第一个策略失效的问题。通过这种双保险机制,有效地保证了缓存数据的可靠性和实时性;
缓存满了怎么办?
- 给缓存服务,选择合适的缓存逐出算法,比如最常见的LRU;
- 针对当前设置的容量,设置适当的警戒值,比如10G的缓存,当缓存数据达到8G的时候,就开始发出报警,提前排查问题或者扩容;
- 给一些没有必要长期保存的key,尽量设置过期时间;
缓存数据丢失怎么办?
如果不允许,就需要带持久化功能的缓存服务来支持,比如Redis;
根据业务对丢失时间的容忍度,还可以选择更具体的持久化策略,比如Redis的RDB或者AOF;
缓存被击穿怎么办?
“击穿”:缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮;
方案:在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。