Redis不设置过期时间采用逻辑过期解决缓存击穿的代码
时间: 2023-12-03 18:05:24 浏览: 158
Redis 对过期数据的处理方法
好的,下面是解决缓存击穿问题的逻辑过期代码:
```
public class RedisUtil {
private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
private static final Long SUCCESS = 1L;
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final String LOCK_PREFIX = "redis_lock_";
private static final String OK = "OK";
private static final String UNLOCK_LUA;
static {
StringBuilder sb = new StringBuilder();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redis.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
private JedisPool jedisPool;
public RedisUtil(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 获取分布式锁
*
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
jedis.close();
return OK.equals(result);
}
/**
* 释放分布式锁
*
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public boolean releaseDistributedLock(String lockKey, String requestId) {
Jedis jedis = jedisPool.getResource();
Object result = jedis.eval(UNLOCK_LUA, Collections.singletonList(lockKey), Collections.singletonList(requestId));
jedis.close();
return SUCCESS.equals(result);
}
/**
* 获取缓存
*
* @param key 缓存key
* @return 缓存值
*/
public String get(String key) {
Jedis jedis = jedisPool.getResource();
String value = jedis.get(key);
jedis.close();
return value;
}
/**
* 设置缓存
*
* @param key 缓存key
* @param value 缓存值
* @return 是否设置成功
*/
public boolean set(String key, String value) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(key, value);
jedis.close();
return OK.equals(result);
}
/**
* 设置缓存并带过期时间
*
* @param key 缓存key
* @param value 缓存值
* @param expireTime 过期时间,单位秒
* @return 是否设置成功
*/
public boolean setWithExpireTime(String key, String value, int expireTime) {
Jedis jedis = jedisPool.getResource();
String result = jedis.setex(key, expireTime, value);
jedis.close();
return OK.equals(result);
}
/**
* 缓存逻辑过期
*
* @param key 缓存key
* @param expireSeconds 过期时间,单位秒
* @param getDataFunc 获取数据的函数
* @return 缓存值
*/
public String getOrSetWithExpireLogic(String key, int expireSeconds, Supplier<String> getDataFunc) {
String value = get(key);
if (value == null) {
// 获取分布式锁
String requestId = UUID.randomUUID().toString();
boolean lockResult = tryGetDistributedLock(LOCK_PREFIX + key, requestId, expireSeconds * 1000);
if (lockResult) {
// 获取数据
value = getDataFunc.get();
if (value != null) {
// 设置缓存并带过期时间
setWithExpireTime(key, value, expireSeconds);
logger.info("set cache success, key={}, expireSeconds={}", key, expireSeconds);
}
// 释放分布式锁
releaseDistributedLock(LOCK_PREFIX + key, requestId);
} else {
// 获取锁失败,等待一段时间后重试
try {
Thread.sleep(100);
} catch (InterruptedException e) {
logger.error("线程等待异常", e);
}
// 递归调用自身
return getOrSetWithExpireLogic(key, expireSeconds, getDataFunc);
}
}
return value;
}
}
```
这段代码实现了一个缓存逻辑过期的功能。当缓存失效时,先获取分布式锁,然后再次检查缓存是否存在,如果不存在,则执行获取数据的函数,然后设置缓存并带过期时间,最后释放分布式锁。如果获取分布式锁失败,则等待一段时间后重试。这样可以避免缓存击穿的问题。
阅读全文