Redis 应用篇二(Redis 分布式锁)

it2025-05-16  9

Redis 分布式锁

本博客使用第三方开源组件Jedis实现Redis客户端,且只考虑Redis服务端单机部署的场景。

什么是分布式锁

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。

分布式锁的实现方式

数据库锁(包括数据库唯一约束/乐观锁),Redis 锁,ZK 锁

如何设计一把可靠的分布式锁

互斥性。在任意时刻,只有一个客户端能持有锁。不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了

jedis实现

<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>

 

 如下代码即为redis 加锁代码,使用jedis.set 方法将redis 的setNx 命令和 expire 命令绑定在一个原子操作中,避免在执行setNx 后系统宕机导致死锁。

clientId 意义为线程的标识,避免在解锁的过程中A 线程解了B 线程的锁具体过程在下文会有解释

public class RedisLock { /** * redis setNx 命令 */ private final String REDIS_COMMAND_NX = "NX"; private final String REDIS_COMMAND_EXPIRE = "PX"; /** * 获取redis 分布式锁 * * @param [jedis, lockKey, clientId, expireTime] * @return boolean * @author longcheng * @date 2020/10/21 23:13 */ public boolean tryLock(Jedis jedis, String lockKey, String clientId, int expireTime) { String result = jedis.set(lockKey, clientId, REDIS_COMMAND_NX, REDIS_COMMAND_EXPIRE, expireTime); if(result.equals("OK")){ return true; }else { return false; } } }

不可靠示例1

如下代码假如执行完整个减库存方法需要15s(在系统压力较大的情况下,正常情况只需要8秒) ,现有三个线程 A,B,C, 当线程A 执行到减库存方法时花费了10秒,此时锁被释放。B 进入加锁,A执行释放锁。此时 是不是就把B 的锁释放掉了? 所以上述加锁代码中的clientId 是否就好理解了

/** * 减库存方法 * * @return */ public Integer reduceStock(Integer num) { String goodsId = "001"; Integer row = 0; try { //加锁 Long res = jedis.setnx("goodsId", "ok"); jedis.expire("goodsId", 10); if (res == 0) { //代表已经被锁住 throw new Exception("已被锁住"); } //减库存 row = goodsDAO.reduceStock(num); } finally { //释放锁 jedis.del(goodsId); } return row; }

 

最新回复(0)