技术简介
Redis 分布式锁是一种利用 Redis 的特性来实现的分布式锁机制,主要用于解决在分布式系统中多个实例对共享资源的并发访问问题。通过使用 Redis 作为锁的存储介质,可以确保在多个服务实例之间的互斥访问。
Redis 分布式锁的基本原理:
- 加锁:通过 SETNX 命令,尝试设置一个锁的键。如果设置成功,表示获得锁;如果失败,表示锁已经被其他实例持有。
- 设置过期时间:为了防止死锁,通常在加锁时会设置一个过期时间,确保即使持锁的实例崩溃,锁也会在一定时间后自动释放。
- 解锁:在完成对共享资源的操作后,释放锁。解锁时需要确保只有持有锁的实例才能解锁,通常通过比较锁的值来实现。
Redis 分布式锁的注意事项:
- 锁过期时间: 设置合理的锁过期时间,避免死锁。
- 释放: 确保在使用完锁后及时释放锁,避免资源浪费。
- 重入锁: 如果需要同一个线程多次获取锁,可以使用可重入锁。
- 公平锁: 如果需要按照请求顺序分配锁,可以使用公平锁
- 锁竞争: 在高并发场景下,可能会出现锁竞争,需要考虑如何处理锁竞争。
示例说明
编写工具类 RedisLockUtil,代码如下:
public class RedisLockUtil {
public static final String LOCK_KEY_PREFIX = "lock:";
private static final String UNLOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
private static final RedisScript<Long> UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLOCK_LUA_SCRIPT, Long.class);
/**
* 尝试Redis锁
*
* @param lockKey 锁名称
* @param lockId 锁持有者唯一ID
* @param duration 过期时间(过期自动释放锁)
* @param timeUnit 时间单位
* @return true: 获取锁成功 , false: 获取锁失败
*/
public static boolean tryLock(@NonNull String lockKey, @NonNull String lockId, long duration, @NonNull TimeUnit timeUnit) {
return RedisUtil.setIfAbsent(LOCK_KEY_PREFIX.concat(lockKey), lockId, duration, timeUnit);
}
/**
* 释放Redis锁
*
* @param lockKey 锁名称
* @param lockId 锁持有者唯一ID
*/
public static boolean unlock(@NonNull String lockKey, @NonNull String lockId) {
Object result = RedisUtil.getRedisTemplate().execute(UNLOCK_REDIS_SCRIPT, Collections.singletonList(LOCK_KEY_PREFIX.concat(lockKey)), lockId);
return Long.valueOf(1).equals(result);
}
}
核心工具类逻辑:
/**
* 仅在Redis中键不存在时设置键的值并设置过期时间。
*
* @param redisKey Redis中的键。
* @param value 要设置的值。
* @param expire 键的过期时间。
* @param timeUnit 过期时间的时间单位。
* @return 如果成功设置了键值对,则返回true,否则返回false。
*/
public static boolean setIfAbsent(@NonNull String redisKey, Object value, long expire, TimeUnit timeUnit) {
return checkBool(getRedisTemplate().opsForValue()
.setIfAbsent(redisKey, value, expire, timeUnit));
}
使用示例:
public void doSomething() {
// 分布式锁标识,根据该标识进行加锁和解锁
String lockKey = "myLockKey";
// 随机ID,解锁时用于校验
String lockId = UUID.randomUUID().toString();
try {
// 创建一个60秒后自动过期的锁. 返回true: 获取锁成功,返回false:获取锁失败(开发者自行决定获取锁失败时处理方案)
boolean status = RedisLockUtil.tryLock(lockKey, lockId, 60, TimeUnit.SECONDS);
// 获取锁失败
if (!status) {
// 执行获取锁失败的业务处理逻辑
throw ApiException.createEx(ExceptionCodeEnum.REDIS_TRY_GET_LOCK_FAILURE, lockKey);
}
// 执行获取锁成功后的业务逻辑
// ...
} finally {
// 解锁状态,true:解锁成功,false:解锁失败
boolean unlockStatus = RedisLockUtil.unlock(lockKey, lockId);
if (!unlockStatus) {
log.error("[Redis分布式锁] 解锁失败, lockKey={}, lockId={}", lockKey, lockId);
}
}
}
总结陈词
此篇文章介绍了 Redis 分布式的基础应用,仅供学习参考。
Redis 分布式锁是一种简单高效的实现分布式锁的方案,可以有效解决分布式系统中数据冲突问题。选择合适的实现方法,注意相关注意事项,可以确保锁的可靠性和性能。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。