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

Redis 分布式锁

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

什么是分布式锁

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

分布式锁的实现方式

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

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

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

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;
    }

正确示例

//注入redislock ,上文的redisLock 工具类
private RedisLock redisLock;

/**
     * 减库存方法
     *
     * @return
     */
    public Integer reduceStock(Integer goodsId) {
       
        Integer row = 0;
        try {
            //加锁  Jedis jedis, String lockKey, String clientId, int expireTime
           boolean res = redisLock.tryLock(jedis,"lock",goodsId,10);
            
            if (!res) {
                //代表已经被锁住
                throw new Exception("已被锁住");
            }
            //减库存
            row = goodsDAO.reduceStock(goodsId);
        } finally {
            //释放锁
            String lockGoodId = jedis.get("lock");
            if (lockGoodId = goodsId) {
                jedis.del(goodsId);
            }
           
        }
        return row;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值