Redis实现分布式锁
1 Redis实现分布式锁
通过Redis的setnx命令来实现分布式锁。
setnx: 如果key不存在,则设置key的值为value,返回1;如果key已经存在,则返回0。
# 加锁 setnxlock:service_id(或者ProductId) 唯一性ID
setnx lock:service_id info
# 释放锁
del key
1.1 Redis单节点实现方案
- 创建对应productId的锁(设置过期时间)
- 假如其他线程也想要操作对应productId的数据,会先去获取锁,如果获取成功,则可以进行操作,否则等待。
- 操作完成后,释放锁。
1.2 Redis分布式锁死锁问题
- 问题:如果加锁始终无法释放,会导致死锁。
- 解决方案:加锁时设置过期时间ttl,保证锁最终能够被释放。
1.3 Redis分布式锁过期时间的问题
- 问题1:如果业务操作没有执行完,锁过期了,会导致其他线程获取锁,导致数据不一致。
- 解决方案:看门狗机制,每隔一段时间,给锁续期。
import redis.clients.jedis.Jedis;
public class RedisLock {
private Jedis jedis;
private String lockKey;
private String lockValue;
private Thread renewThread;
private volatile boolean isLocked = false;
public RedisLock(Jedis jedis, String lockKey, String lockValue) {
this.jedis = jedis;
this.lockKey = lockKey;
this.lockValue = lockValue;
}
public boolean tryLock(long timeout) {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < timeout) {
if (jedis.setnx(lockKey, lockValue) == 1) {
isLocked = true;
startRenewThread();
return true;
}
}
return false;
}
public void unlock() {
if (!isLocked) {
return;
}
isLocked = false;
renewThread.interrupt();
if (lockValue.equals(jedis.get(lockKey))) {
jedis.del(lockKey);
}
}
private void startRenewThread() {
renewThread = new Thread(() -> {
while (isLocked) {
jedis.expire(lockKey, 30);
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
renewThread.start();
}
}
- 问题2:我误删别人的锁,导致数据不一致。
- 解决方案:我getKey的时候,判断一下是不是自己加的值,是的话再删除。
1.4 Redis分布式锁的 性能 问题(分段锁)
- 问题1:当有一把把锁因为业务操作时间过长,导致锁的续期线程不断续期,会导致Redis的性能问题。
- 解决方案:将锁分段(类似ConcurrentHashMap),每一段锁对应一个Redis实例,将锁的数量分散到多个Redis实例上。
比如:1000个锁,分成10段,每一段100个锁,每一段对应一个Redis实例。(每个锁的名字都叫Product_id_1…Product_id_n)
我的Github地址,欢迎大家加入我的开源项目,或者(在我的主页联系我)加入你们的开源项目,点点Github-Stars。
\ | 开源项目名称 | 依赖类型 | 版本号 | 描述 |
---|---|---|---|---|
1 | spring-boot-starter-trie | pom | 1.0.0-SNAPSHOT | 特定需求下查询速度远超开源检索工具,innodb下B+树或者ES中倒排索引无法与之比拟. |
2 | spring-boot-starter-trie | jar | 1.0.0-M1 | 提供了基于SpringCloud的服务节点,可以通过Nacos注册中心进行服务发现,实现了树的动态扩容与缩容,以及服务的动态上下线。 |
3 | Data-Provider | pom | 1.0.0-SNAPSHOT | 提供了多种数据源的查询,以及数据的类型同步,作为一个Jar可以依赖在其他服务上动态的提供数据。 |