title: 两级缓存实现分析之缓存设置 tags:
- J2Cache
- 缓存
- 雪崩
- 缓存击穿
- 缓存失效 categories: 工作日志 date: 2017-06-25 18:18:54
上篇描述了缓存过期的代码
本次讨论一下两级缓存如何获取缓存设置,移除,获取
正如文章描述缓存读取顺序
读取顺序 -> L1 -> L2 -> DB复制代码
先描述一下缓存读取代码:
/** * 获取缓存中的数据 * * @param region : Cache Region name * @param key : Cache key * @return cache object */ public CacheObject get(String region, Object key) { CacheObject obj = new CacheObject(); obj.setRegion(region); obj.setKey(key); if (region != null && key != null) { obj.setValue(CacheManager.get(LEVEL_1, region, key)); if (obj.getValue() == null) { obj.setValue(CacheManager.get(LEVEL_2, region, key)); if (obj.getValue() != null) { obj.setLevel(LEVEL_2); CacheManager.set(LEVEL_1, region, key, obj.getValue()); } } else obj.setLevel(LEVEL_1); } return obj; }复制代码
首先在localcache中查询,如果可以获得缓存直接返回,
如果不能则去remotecache获取如果可以获得缓存 将localcache设置缓存后直接返回,
如果不能则返回空(CacheObject中的value为null)===》可以实现更多级缓存
这边隐含一个条件(高一级缓存必定拥有比第一级缓存更多并且准确的数据)
此处存在一个缓存击穿的问题:
我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直直接查询然后再缓存查询结果返回。这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了。
无论多级缓存都存在此问题,对于此处的解决方案建议对使用缓存的返回结果包装(Optional)可以使用google或者java8的可选对象,如果db查询或者计算结果为空,显示的返回Absent对象,可以避免大量无效的key导致的缓存击穿问题
下面介绍缓存的设置实现
public void set(String region, Object key, Object value, Integer expireInSec) { if (region != null && key != null) { if (value == null) evict(region, key); else { // 分几种情况 // Object obj1 = CacheManager.get(LEVEL_1, region, key); // Object obj2 = CacheManager.get(LEVEL_2, region, key); // 1. L1 和 L2 都没有 // 2. L1 有 L2 没有(这种情况不存在,除非是写 L2 的时候失败 // 3. L1 没有,L2 有 // 4. L1 和 L2 都有 _sendEvictCmd(region, key);// 清除原有的一级缓存的内容 CacheManager.set(LEVEL_1, region, key, value, expireInSec); CacheManager.set(LEVEL_2, region, key, value, expireInSec); } } // log.info("write data to cache region="+region+",key="+key+",value="+value); }复制代码
正如上文所述,高级缓存(L2)必然承载更多低级缓存所不存在的数据
首先发起命令清除所有一级缓存的对应key(保证其他一级缓存的值已被清除,不会出现缓存不一致)
分别在各级缓存设置对应的key和value
对于db或者其他计算资源来说如果过载会发生较为严重的后果,比如无返回,超时甚至宕机。
缓存雪崩就是可能出现的原因之一。
如果出现某个缓存在一段时间后过期了,同时出现高并发获取该缓存将会出现缓存雪崩,
首先查询该缓存发现不存在后立刻去向db获取数据,该处可能将一瞬间的流量完全打到db上
导致db负债过高使得应用超时乃至宕机
通常解决方案只能通过getCache时如果key不存在就增加一把锁,使得其他线程必须等待到锁释放
当然还有缓存失效问题
用户在初次使用系统时,可能要针对该用户存储大量缓存,设想场景均为24h,那么当24h后所有缓存过期
同样可能出现大量流量打到db出现异常。
通常解决方案是在过期时间上加上一定长度的随机数,使得缓存不至于一刹那全部过期导致db流量过大