系统现状:
  由于系统目前提醒是在公共模块,对于提醒的数目实时计算。对于db的压力较大。因此采用J2cache的两级缓存。关于两级缓存的说明
  但是当部分业务更新时需要将缓存删除,导致系统在某个时间点出现db负载过大。
  
  对应业务系统出现大量慢sql以及由于超时无法完成的请求
  
  出现了系统雪崩的场景。
  两级缓存实现分析之缓存设置   ===》当大量请求提醒的业务sql过来导致系统无法及时的响应。而提醒sql又出现超时是的无法真正的缓存下来。使的sql越积越多,db负载过高。直到有成功将缓存写入或者db被击穿。
  对策:
     - 改写sql,尽量提高sql效率 
- 在缓存处加分布式锁,避免同时回源
第一个策略对于系统并没有得到很好的缓解。主要是由于该发生提醒缓存失效使用的用户较多,用户频繁请求数据,导致结果无法在有效的h时间内缓存,出现大量慢sql请求直至系统超时。
  那么我们要做的就是在缓存上增加blockingcache的功能。
  那么何为blockingCache呢?需要哪些功能呢?
     - 对于系统同一个缓存在多个请求过来时能hold住其他请求,单个请求执行业务
- 防止出现死锁,在系统挂掉的情况下锁能够自动释放
- 可定义的超时时长
- 其他请求在缓存中设置过值之后自动苏醒,返回缓存结果
- 支持可重入
- 其他配置需求等等
 
  由上述可知,我们在缓存get时需要处理的事情如下:
     - 先取缓存,如果有结果直接返回
- 如果没有缓存,直接加锁
- 如果加锁成功直接返回null  ===》需要去执行业务逻辑
- 如果加锁不成功或者不可重入 则线程需要等待
- 线程每隔指定时间sleep结束(默认为300ms)最多等待60s,每次需要检查是否缓存中结果如果有结果直接返回
- 还需要检查是否可以加锁成功(如果加锁成功返回null)
查看原先的get的代码如下
  public Object get(Object key) throws CacheException {     if (null == key)         return null;     Object obj = null;     try {         byte[] b = redisCacheProxy.hget(region2, getKeyName(key));         if (b != null)             obj = SerializationUtils.deserialize(b);     } catch (Exception e) {         log.error("Error occured when get data from redis2 cache", e);         if (e instanceof IOException || e instanceof NullPointerException)             evict(key);     }     return obj; }
  改造后代码如下
  public Object get(Object key) throws CacheException {     if (null == key)         return null;     Object obj = null;     try {         byte[] keyName = getKeyName(key);         byte[] b = redisCacheProxy.hget(region2, keyName);         if (b != null) {             obj = SerializationUtils.deserialize(b);         } else if (redisCacheProxy.isBlock()) {             byte[] lockKey = getLockKey(key);             boolean locked = getLock(lockKey);             if (locked || canReentrant(key)) {                 return null;             } else {                 int timeLeft = redisCacheProxy.getTimeOutMillis();                 while (timeLeft > 0) {                     Thread.sleep(redisCacheProxy.getTimeWaitMillis());                     timeLeft -= redisCacheProxy.getTimeWaitMillis();                     b = redisCacheProxy.hget(region2, keyName);                     if (b != null) {                         obj = SerializationUtils.deserialize(b);                         break;                     } else {                         //如果拿不到再尝试一次获取lock,防止出现部分情况一直没有put导致等待时间过长。后续要改造成可重入                         if (getLock(lockKey)) {                             return null;                         }                     }                     //超时是应该抛异常呢还是直接返回null? 目前返回null                 }             }           }     } catch (Exception e) {         log.error("Error occured when get data from redis2 cache", e);         if (e instanceof IOException || e instanceof NullPointerException)             evict(key);     }     return obj; }   private boolean canReentrant(Object key) {     //对于缓存来说要求不精确,使用线程id即可     try {         String value = redisCacheProxy.get(getLockKeyString(key));         if (value != null) {             long oriThreadId = Long.parseLong(value);             return oriThreadId == Thread.currentThread().getId();         }     } catch (Exception ex) {         log.error(ex.getMessage(), ex);     }     return false; }   private boolean getLock(byte[] lockKey) {     return getLock(lockKey, String.valueOf(Thread.currentThread().getId()).getBytes()); }   private boolean getLock(byte[] lockKey, byte[] keyName) {     return "OK".equals(redisCacheProxy.set(lockKey, keyName, NX, PX, redisCacheProxy.getTimeLockMillis())); }   private void releaseLock(byte[] lockKey) {     redisCacheProxy.del(lockKey); }   private String getLockKeyString(Object key) {     return String.format(lockPattern, region, key.hashCode() % redisCacheProxy.getStripes()); }   private byte[] getLockKey(Object key) {     String keyName = getLockKeyString(key);     return keyName.getBytes(); }
  可以参考
   
  https://git.oschina.net/ld/J2Cache/pulls/48
  https://git.oschina.net/ld/J2Cache/pulls/47
  http://git.oschina.net/ld/J2Cache/commit/928bde1a60b9884f5dff95257a954c61aa2bb367