RedisTemplate 实现分布式锁
基础分布式锁
使用redisTemplate和RedisCallback 实现分布式锁
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import redis.clients.jedis.JedisCommands;
import java.util.Arrays;
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final Logger log = LoggerFactory.getLogger(RedisDistributedLock.class);
/**
* 尝试获取分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识(用于标识锁的持有者)
* @param expireTime 过期时间(秒)
* @return 是否获取成功
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) {
byte[] value = redisTemplate.getStringSerializer().serialize(requestId);
String lockValue = new String(value);
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
String result = commands.set(lockKey, lockValue, "NX", "EX", expireTime);
return "OK".equals(result);
}
});
}
/**
* 释放分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识
* @return 是否释放成功
*/
public boolean releaseLock(String lockKey, String requestId) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) {
byte[] value = redisTemplate.getStringSerializer().serialize(requestId);
String lockValue = new String(value);
// 使用Lua脚本保证原子性:只有锁的持有者才能释放锁
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else " +
"return 0 " +
"end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(luaScript);
redisScript.setResultType(Long.class);
try {
Long result = redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockValue);
if (result != null && result == 1L) {
log.info("Redis lock released successfully: {}", lockKey);
} else {
log.warn("Failed to release Redis lock (possibly held by another client): {}", lockKey);
}
return result != null && result == 1;
} catch (Exception e) {
log.error("Failed to release Redis lock: {}", lockKey, e);
}
return false;
}
});
}
}
RedisDistributedLock是一个基于 Spring Data Redis 实现的分布式锁组件,用于在分布式系统中协调多个服务实例对共享资源的访问控制。
使用场景
适用于需要分布式协调的场景:
-
订单处理防重复提交
-
库存扣减防超卖
-
定时任务分布式调度
-
分布式系统关键资源互斥访问
优势特点
-
简单易用 - 提供简洁的 API 接口
-
安全可靠 - 完善的锁机制防止常见并发问题
-
可追溯性 - 通过请求标识追踪锁的持有者
-
容错处理 - 完善的异常处理和日志记录
该组件为分布式系统提供了轻量级且可靠的分布式锁解决方案,能够有效解决分布式环境下的资源竞争问题。
增强版分布式锁
支持重试和自动续约的分布式锁
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import redis.clients.jedis.JedisCommands;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.connection.RedisConnection;
import javax.annotation.PreDestroy;
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final Logger log = LoggerFactory.getLogger(RedisDistributedLock.class);
// 用于锁续约的调度器
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
/**
* 尝试获取分布式锁(带重试机制)
* @param lockKey 锁的key
* @param requestId 请求标识
* @param expireTime 过期时间(秒)
* @param waitTime 最大等待时间(毫秒)
* @param retryInterval 重试间隔(毫秒)
* @return 是否获取成功
*/
public boolean tryLockWithRetry(String lockKey, String requestId, long expireTime,
long waitTime, long retryInterval) {
long startTime = System.currentTimeMillis();
while (true) {
// 尝试获取锁
if (tryLock(lockKey, requestId, expireTime)) {
log.info("Successfully acquired lock: {}", lockKey);
return true;
}
// 检查是否超时
if (System.currentTimeMillis() - startTime > waitTime) {
log.warn("Failed to acquire lock within {} ms: {}", waitTime, lockKey);
return false;
}
// 等待一段时间后重试
try {
log.debug("Waiting to retry acquiring lock: {}", lockKey);
Thread.sleep(retryInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("Thread interrupted while waiting for lock: {}", lockKey);
return false;
}
}
}
/**
* 尝试获取分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识(用于标识锁的持有者)
* @param expireTime 过期时间(秒)
* @return 是否获取成功
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
// 基础分布式锁实现
}
/**
* 启动锁续约机制
* @param lockKey 锁的key
* @param requestId 请求标识
* @param expireTime 续约后的过期时间(秒)
* @param renewInterval 续约间隔(秒)
* @return 续约任务Future,可用于取消续约
*/
public ScheduledFuture<?> startLockRenewal(String lockKey, String requestId,
long expireTime, long renewInterval) {
log.info("Starting lock renewal for: {} with interval {}s", lockKey, renewInterval);
return scheduler.scheduleAtFixedRate(() -> {
try {
boolean renewed = renewLock(lockKey, requestId, expireTime);
if (renewed) {
log.debug("Lock renewed successfully: {}", lockKey);
} else {
log.warn("Failed to renew lock, lock may have been released: {}", lockKey);
// 在实际应用中,这里可以抛出异常或执行其他恢复逻辑
}
} catch (Exception e) {
log.error("Error occurred while renewing lock: {}", lockKey, e);
}
}, renewInterval, renewInterval, TimeUnit.SECONDS);
}
/**
* 续约分布式锁
* @param lockKey 锁的key
* @param requestId 请求标识
* @param expireTime 续约后的过期时间(秒)
* @return 是否续约成功
*/
public boolean renewLock(String lockKey, String requestId, long expireTime) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) {
byte[] value = redisTemplate.getStringSerializer().serialize(requestId);
String lockValue = new String(value);
// 使用Lua脚本保证原子性:只有锁的持有者才能续约
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('expire', KEYS[1], ARGV[2]) " +
"else " +
"return 0 " +
"end";
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText(luaScript);
redisScript.setResultType(Long.class);
try {
Long result = redisTemplate.execute(redisScript, Arrays.asList(lockKey),
lockValue, String.valueOf(expireTime));
boolean success = result != null && result == 1;
if (!success) {
log.warn("Lock renewal failed for: {}", lockKey);
}
return success;
} catch (Exception e) {
log.error("Error executing lock renewal for: {}", lockKey, e);
return false;
}
}
});
}
/**
* 释放分布式锁(带续约任务取消)
* @param lockKey 锁的key
* @param requestId 请求标识
* @param renewalFuture 续约任务Future(可为null)
* @return 是否释放成功
*/
public boolean releaseLock(String lockKey, String requestId, ScheduledFuture<?> renewalFuture) {
// 先取消续约任务
if (renewalFuture != null && !renewalFuture.isCancelled()) {
renewalFuture.cancel(false);
log.debug("Lock renewal task cancelled for: {}", lockKey);
}
// 然后释放锁
return releaseLock(lockKey, requestId);
}
public boolean releaseLock(String lockKey, String requestId) {
// 基础分布式锁实现
}
/**
* 优雅关闭,释放资源
*/
@PreDestroy
public void destroy() {
log.info("Shutting down lock renewal scheduler");
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
使用示例
java
@Service
public class OrderService {
@Autowired
private RedisDistributedLock redisDistributedLock;
public void processOrder(String orderId) {
String lockKey = "order_lock:" + orderId;
String requestId = UUID.randomUUID().toString();
ScheduledFuture<?> renewalFuture = null;
try {
// 尝试获取锁,最多等待3秒,每次重试间隔100ms
boolean lockAcquired = redisDistributedLock.tryLockWithRetry(
lockKey, requestId, 30, 3000, 100);
if (!lockAcquired) {
throw new RuntimeException("Unable to acquire lock for order: " + orderId);
}
// 启动锁续约,每10秒续约一次,续约后过期时间为30秒
renewalFuture = redisDistributedLock.startLockRenewal(
lockKey, requestId, 30, 10);
// 执行业务逻辑
doProcessOrder(orderId);
} finally {
// 释放锁并取消续约
redisDistributedLock.releaseLock(lockKey, requestId, renewalFuture);
}
}
private void doProcessOrder(String orderId) {
// 业务逻辑实现
}
}
新增功能说明
🔄 重试机制
-
tryLockWithRetry()
: 支持在指定时间内自动重试获取锁 -
可配置最大等待时间和重试间隔
-
支持线程中断处理
⏰ 续约机制
-
startLockRenewal()
: 启动定时续约任务 -
renewLock()
: 执行锁续约操作 -
使用 Lua 脚本保证续约的原子性
-
自动管理续约任务的启动和取消