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