Redis 提供了多种方法来实现分布式锁,确保多个进程或机器能够协调地访问共享资源。以下是详细的实现步骤和代码示例。
1. 基于 SET
命令的分布式锁
获取锁
获取锁的核心是使用 SET
命令,并带上 NX
和 EX
选项:
NX
(Not eXists): 仅当键不存在时才设置键。EX
(EXpire): 设置键的过期时间,防止死锁。
redis
# 获取锁示例
SET mylock <lock_value> NX EX 10
Lua 脚本实现
为了更加原子化,可以使用 Lua 脚本:
lua
-- 获取锁的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]
local ttl = tonumber(ARGV[2])
if redis.call("SETNX", lock_key, lock_value) == 1 then
redis.call("EXPIRE", lock_key, ttl)
return 1
else
return 0
end
Redis 命令:
sh
redis-cli EVAL "local lock_key = KEYS[1]; local lock_value = ARGV[1]; local ttl = tonumber(ARGV[2]); if redis.call('SETNX', lock_key, lock_value) == 1 then redis.call('EXPIRE', lock_key, ttl); return 1; else return 0; end" 1 mylock lock_value 10
释放锁
释放锁时需要先检查当前锁是否是自己持有的,然后再删除锁:
lua
-- 释放锁的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]
if redis.call("GET", lock_key) == lock_value then
redis.call("DEL", lock_key)
return 1
else
return 0
end
Redis 命令:
sh
redis-cli EVAL "local lock_key = KEYS[1]; local lock_value = ARGV[1]; if redis.call('GET', lock_key) == lock_value then redis.call('DEL', lock_key); return 1; else return 0; end" 1 mylock lock_value
2. 基于 Redlock 算法的分布式锁
Redlock 是 Redis 作者提出的一种更加健壮的分布式锁算法,旨在在多个 Redis 实例上实现锁的获取和释放。
获取锁
- 获取当前时间。
- 依次尝试在多个 Redis 实例上获取锁。
- 如果在大部分实例上成功获取锁,并且时间小于有效期,则获取锁成功。
lua
-- 基于 Redlock 算法的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]
local ttl = tonumber(ARGV[2])
local start_time = redis.call('TIME')
local success_count = 0
local majority = math.floor(#redis_instances / 2) + 1
for _, redis_instance in ipairs(redis_instances) do
if redis_instance.call("SET", lock_key, lock_value, "NX", "PX", ttl) then
success_count = success_count + 1
end
end
local elapsed_time = (redis.call('TIME')[1] - start_time[1]) * 1000 + (redis.call('TIME')[2] - start_time[2]) / 1000
if success_count >= majority and elapsed_time < ttl then
return 1
else
for _, redis_instance in ipairs(redis_instances) do
redis_instance.call("DEL", lock_key)
end
return 0
end
释放锁
释放锁时需要在所有实例上依次删除锁:
lua
-- 基于 Redlock 算法的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]
for _, redis_instance in ipairs(redis_instances) do
if redis_instance.call("GET", lock_key) == lock_value then
redis_instance.call("DEL", lock_key)
end
end
return 1
3. 使用 Redisson 实现分布式锁
Redisson 是一个基于 Redis 的 Java 客户端,可以方便地进行分布式锁的管理。以下是使用 Redisson 实现分布式锁的示例:
添加 Maven 依赖
xml
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version>
</dependency>
使用 Redisson 获取和释放锁
java
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import java.util.concurrent.TimeUnit;
public class DistributedLockExample {
public static void main(String[] args) {
// 配置 Redis 服务器
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
// 创建 Redisson 客户端
RedissonClient redisson = Redisson.create(config);
// 获取锁
RLock lock = redisson.getLock("myLock");
try {
// 尝试加锁,等待时间 100ms,锁定时间 10s
boolean isLocked = lock.tryLock(100, 10000, TimeUnit.MILLISECONDS);
if (isLocked) {
try {
// 执行需要锁保护的代码
System.out.println("Lock acquired!");
// 加锁后的操作
} finally {
// 释放锁
lock.unlock();
}
} else {
System.out.println("Unable to acquire lock");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 关闭 Redisson 客户端
redisson.shutdown();
}
}
}
总结
通过合理使用 Redis 提供的 SETNX
命令、Lua 脚本、Redlock 算法以及 Redisson 客户端,可以有效地实现分布式锁。这些方法各有优缺点,开发者可以根据具体的应用场景选择最合适的方案来确保分布式系统中的数据一致性和并发控制。