分布式锁+lua解锁,原子性
拿java来做对比,工具类
java
@Component
public class RedisUtil {
private static StringRedisTemplate redisTemplate;
@Autowired
public void setRedisTemplate(StringRedisTemplate redisTemplate) {
RedisUtil.redisTemplate = redisTemplate;
}
private static final String LOCK_PREFIX = "lock:";
private static final String NULL_MARKER = "NULL"; // 空值标记
// 雪花算法生成唯一锁值
private static String lockValue() {
return IdUtil.getSnowflakeNextIdStr();
}
// 加锁
public static String lock(String key, int expireSeconds) {
String lockKey = LOCK_PREFIX + key;
String value = lockValue();
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(lockKey, value, expireSeconds, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success) ? value : null;
}
// 解锁(Lua 原子)
public static void unlock(String key, String value) {
String lockKey = LOCK_PREFIX + key;
String lua = "if redis.call('get', KEYS[1]) == ARGV[1] " +
"then return redis.call('del', KEYS[1]) " +
"else return 0 end";
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(lua);
script.setResultType(Long.class);
redisTemplate.execute(script, Collections.singletonList(lockKey), value);
}
// 获取(自动处理空值标记)
public static <T> T get(String key, Class<T> clazz) {
Object value = redisTemplate.opsForValue().get(key);
if (value == null) {
return null;
}
if (NULL_MARKER.equals(value)) {
return null; // 空值缓存,返回 null
}
return objectMapper.convertValue(value, clazz);
}
// 存数据
public static void set(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
// 存空值(缓存穿透保护)
public static void setNull(String key, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, NULL_MARKER, timeout, unit);
}
// 缓存数据(自动序列化)
private static final ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule());
public static void set(String key, Object value, long timeout, TimeUnit unit) {
try {
String json = objectMapper.writeValueAsString(value);
redisTemplate.opsForValue().set(key, json, timeout, unit);
} catch (JsonProcessingException e) {
throw new RuntimeException("序列化失败", e);
}
}
}
业务层
java
public Product getProduct(Long productId) {
String cacheKey = "product:info:" + productId;
String lockKey = "lock:product:" + productId;
// 1. 先查缓存(包含空值缓存)
Product product = redisUtil.get(cacheKey, Product.class);
if (product != null) {
return product;
}
// 2. 自旋重试
int maxRetry = 5;
for (int i = 0; i < maxRetry; i++) {
// 再次检查缓存
product = redisUtil.get(cacheKey, Product.class);
if (product != null) {
return product;
}
// 3. 获取分布式锁
String lockValue = redisUtil.lock(lockKey, 10);
if (lockValue != null) {
try {
// 双重检查
product = redisUtil.get(cacheKey, Product.class);
if (product != null) {
return product;
}
// 4. 查数据库
product = productMapper.selectById(productId);
// 5. 缓存结果
if (product != null) {
redisUtil.set(cacheKey, product, 30, TimeUnit.MINUTES);
} else {
// 缓存空值,短过期
redisUtil.setNull(cacheKey, 1, TimeUnit.MINUTES);
}
return product;
} finally {
redisUtil.unlock(lockKey, lockValue);
}
}
// 6. 等待重试(指数退避)
try {
Thread.sleep(50L * (i + 1));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 7. 兜底:直接查库
return productMapper.selectById(productId);
}