之前使用二级缓存来解决 热点key失效导致缓存击穿问题
但是二级缓存缺点:redis内存使用率太低
为了解决缓存使用率太低的问题,改成双key的方案
- 将缓存值和缓存的时间分开存放,这样每次查询的时候,先查询缓存时间是否存在,
- 缓存时间不存在则里面更新一下缓存时间并且异步去更新缓存值
- 缓存时间存在则 直接使用缓存值返回
- 代码逻辑如下
public static boolean set(String key, String value, int seconds) { Jedis jedis = null; try { jedis = jedisPool.getResource(); if (seconds > 0){ // 添加数据缓存,缓存有效时间 = 真实时间 + 1 天 jedis.set(key, seconds + 60 * 60 * 24, value); // 添加过期时间缓存,缓存有效时间 = 真实时间 jedis.set("lock_" + key, seconds, System.currentTimeMillis() + ""); } else { jedis.set(key, value); jedis.set("lock_" + key, System.currentTimeMillis() + ""); } return true; } catch (JedisException e) { if (jedis != null) { returnBrokenResource(jedis); jedis = null; } throw e; } finally { if (jedis != null) { returnResource(jedis); } } }
public static String get(String key) { Jedis jedis = null; try { jedis = jedisPool.getResource(); // 缓存过期 && 获取锁成功,setnx:原子操作 if (jedis.setnx("lock_" + key, System.currentTimeMillis() + "") == 1) { /** * 将锁的失效时间设为60s,在60s内若查询数据库成功,则更新锁的失效时间=缓存时间 * 如果60s内出现异常,则60s后第一个请求又会去访问数据库 * 返回null表示没有查询到数据库,外层代码会通过数据库获取数据并设置缓存 */ jedis.expire("lock_" + key, 60); return null; } else{ // 缓存未过期或者缓存过期但获取锁失败, 则返回旧数据 return jedis.get(key); } } catch (JedisException e) { if (jedis != null) { returnBrokenResource(jedis); jedis = null; } throw e; } finally { if (jedis != null) { returnResource(jedis); } } }