Redis实现分布式锁

37 篇文章 0 订阅
10 篇文章 0 订阅





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单节点实现方案

  1. 创建对应productId的锁(设置过期时间)
  2. 假如其他线程也想要操作对应productId的数据,会先去获取锁,如果获取成功,则可以进行操作,否则等待。
  3. 操作完成后,释放锁。

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。

\开源项目名称依赖类型版本号描述
1spring-boot-starter-triepom1.0.0-SNAPSHOT特定需求下查询速度远超开源检索工具,innodb下B+树或者ES中倒排索引无法与之比拟.
2spring-boot-starter-triejar1.0.0-M1提供了基于SpringCloud的服务节点,可以通过Nacos注册中心进行服务发现,实现了树的动态扩容与缩容,以及服务的动态上下线。
3Data-Providerpom1.0.0-SNAPSHOT提供了多种数据源的查询,以及数据的类型同步,作为一个Jar可以依赖在其他服务上动态的提供数据。
  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小小狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值