一、核心实现原理
RedisTemplate 分布式锁基于 Redis 的原子操作实现,核心流程如下:
-
加锁:通过
SET key value NX PX timeout命令原子化设置锁(NX表示键不存在时设置,PX设置过期时间)。 -
解锁:通过 Lua 脚本保证删除锁的原子性(验证锁持有者身份后删除)。
-
续期:通过后台线程定期延长锁的过期时间,防止业务执行超时导致锁提前释放。
二、完整代码实现
1. 依赖配置(Maven)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. RedisTemplate 配置
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
3. 分布式锁工具类
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final long DEFAULT_EXPIRE = 30_000; // 默认30秒
// 加锁(带UUID防误删)
public String tryLock(String lockKey, long expire) {
String uuid = UUID.randomUUID().toString();
Boolean success = redisTemplate.opsForValue().setIfAbsent(
LOCK_PREFIX + lockKey,
uuid,
expire,
TimeUnit.MILLISECONDS
);
return success != null && success ? uuid : null;
}
// 解锁(Lua脚本保证原子性)
public boolean unlock(String lockKey, String uuid) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
Long result = redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Arrays.asList(LOCK_PREFIX + lockKey),
uuid
);
return result != null && result == 1L;
}
// 续期(看门狗机制)
public boolean renewLock(String lockKey, String uuid, long expire) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('pexpire', KEYS[1], ARGV[2]) " +
"else " +
" return 0 " +
"end";
Long result= redisTemplate.execute(
new DefaultRedisScript<>(script, Long.class),
Arrays.asList(LOCK_PREFIX + lockKey),
uuid, expire
) ;
return result!= null && result == 1L;
}
}
三、使用示例
1. 业务代码集成
@Service
public class OrderService {
@Autowired
private RedisDistributedLock lockUtil;
public void createOrder() {
String lockKey = "order:create";
String lockValue = lockUtil.tryLock(lockKey, 10_000); // 10秒过期
if (lockValue != null) {
try {
// 执行业务逻辑(如扣减库存)
System.out.println("处理订单中...");
Thread.sleep(5_000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lockUtil.unlock(lockKey, lockValue); // 释放锁
}
} else {
System.out.println("获取锁失败,稍后重试");
}
}
}
2. 自动续期(可选)
// 启动续期线程(需结合定时任务或线程池)
public void startRenewalTask(String lockKey, String uuid, long expire) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
if (lockUtil.renewLock(lockKey, uuid, expire)) {
System.out.println("锁续期成功");
} else {
System.out.println("锁已释放或续期失败");
}
}, expire / 3, expire / 3, TimeUnit.MILLISECONDS); // 每1/3过期时间续期一次
}
四、关键优化点
1. 防误删锁
-
UUID 标识:每个锁绑定唯一 UUID,解锁时验证持有者身份,避免 A 线程释放 B 线程的锁。
-
Lua 脚本:通过原子化脚本确保
GET和DEL操作的原子性。
2. 高并发优化
-
自旋重试:加锁失败时短暂休眠后重试,避免频繁阻塞线程。
public String tryLockWithRetry(String lockKey, long expire, int retryTimes) { for (int i = 0; i < retryTimes; i++) { String uuid = tryLock(lockKey, expire); if (uuid != null) return uuid; try { Thread.sleep(100); } catch (InterruptedException ignored) {} } return null; }
3. 性能优化
-
连接池配置:使用 Lettuce 连接池提升吞吐量。
spring: redis: lettuce: pool: max-active: 50 max-idle: 10 min-idle: 2
五、注意事项
-
锁过期时间:需大于业务执行时间,建议设置业务时间的 2-3 倍。
-
Redis 高可用:单节点 Redis 存在单点故障风险,建议使用 Redis Cluster 或 Redlock 算法。
-
异常处理:确保
finally块中释放锁,避免锁泄漏。 -
监控指标:监控锁的获取成功率、平均持有时间、续期频率等。
六、与 Redisson 对比
|
特性 |
RedisTemplate 手动实现 |
Redisson |
|---|---|---|
|
开发成本 |
高(需手动处理续期、异常等) |
低(封装完善) |
|
功能完整性 |
基础功能(需自行扩展) |
支持可重入锁、公平锁、看门狗等 |
|
性能 |
依赖代码优化 |
高度优化 |
|
适用场景 |
需要定制化锁策略的项目 |
快速实现标准分布式锁的场景 |
七、总结
通过 RedisTemplate 实现分布式锁需重点关注 原子性操作 和 锁生命周期管理。核心步骤包括:
-
使用
setIfAbsent原子化加锁。 -
通过 Lua 脚本安全解锁。
-
可选续期机制防止业务超时。
对于复杂场景(如可重入锁、公平锁),建议直接使用 Redisson 库以减少开发成本。
826

被折叠的 条评论
为什么被折叠?



