分布式锁的介绍(Redis,Mqsql,Zookeeper)

7 篇文章 0 订阅
1 篇文章 0 订阅

分布式锁,说简单就是在分布式项目上的用的锁

给大家介绍三种分布式锁的实现方案

  • Mysql的乐观锁
  • Redis的分布式锁
  • Zookeeper的分布式锁
  1. 数据库的乐观锁:基于数据表添加某个字段,又称版本号,每次对数据进行操作版本号都有所改变,这样多线程同时操作的时候的判断条件多了个版本号的判断,就可以防止一定的安全问题.(代码可见文章底部)
  2. 乐观锁:之所以叫乐观,是在线程访问的时候总是认为没有其它线程访问,所以没有上锁,但是会在更新的时候通过版本号机制进行判断;
  3. 反之,悲观锁认为每次都是多线程在访问,每次都加锁,别人在访问的时候就会一直等待直到拿到锁,这样会导致效率上的问题(阻塞)(数据表的行锁,表锁以及synchronized的实现都是悲观锁)
-- 可能会发生的异常情况
-- 线程1查询,当前left_count为1,则有记录
select * from t_bonus where id = 10001 and left_count > 0

-- 线程2查询,当前left_count为1,也有记录
select * from t_bonus where id = 10001 and left_count > 0

-- 线程1完成领取记录,修改left_count为0,
update t_bonus set left_count = left_count - 1 where id = 10001

-- 线程2完成领取记录,修改left_count为-1,产生脏数据
update t_bonus set left_count = left_count - 1 where id = 10001

通过乐观锁实现

-- 添加版本号控制字段
ALTER TABLE table ADD COLUMN version INT DEFAULT '0' NOT NULL AFTER t_bonus;

-- 线程1查询,当前left_count为1,则有记录,当前版本号为1234
select left_count, version from t_bonus where id = 10001 and left_count > 0

-- 线程2查询,当前left_count为1,有记录,当前版本号为1234
select left_count, version from t_bonus where id = 10001 and left_count > 0

-- 线程1,更新完成后当前的version为1235,update状态为1,更新成功
update t_bonus set version = 1235, left_count = left_count-1 where id = 10001 and version = 1234

-- 线程2,更新由于当前的version为1235,udpate状态为0,更新失败,再针对相关业务做异常处理
update t_bonus set version = 1235, left_count = left_count-1 where id = 10001 and version = 1234
  1. Redis实现分布式锁:本质就是往Redis中存储数据,只不过使用的命令有些不一样
  2. 通过 4 个命令(下面会解释),判断返回值来进行上锁,判断超时,设置新值,解锁
  3. 上锁就是存入一个key  value  value就是设计的过期时间
  4. 可以使用工具类调用命令,也可以世界使用redisTemplate.xxx 或者 redisTemplate.excute(重写方法(这个比较麻烦,涉及到序列化))
  5. setNX  上锁  成功返回 1   失败返回  0  ==>key存在,不做任何动作
  6. getSET  设置新的值   返回旧值   ==>没有key  返回nil
  7. get   返回现在的value    ==>没有key  返回nil
  8. del   删除
  • 代码实战操作
    /**
     * 加锁
     * @param key redis key
     * @param expire 过期时间,单位秒
     * @return true:加锁成功,false,加锁失败
     */
    public static boolean lock2(String key, int expire) {

        RedisService redisService = SpringUtils.getBean(RedisService.class);

        // 获取当前时间+过期时间,作为新的时间值
        long value = System.currentTimeMillis() + expire;
        // 1.上锁
        long status = redisService.setnx(key, String.valueOf(value));

        if(status == 1) {// 成功上锁
            return true;
        }
        // 2.上锁失败,获取锁中旧的时间值
        long oldExpireTime = Long.parseLong(redisService.get(key, "0"));
        if(oldExpireTime < System.currentTimeMillis()) {// 超时
            // 重新 获取当前时间+过期时间,作为新的时间值
            long newExpireTime = System.currentTimeMillis() + expire;
            // 3.设置新的值,并且返回  当前操作中的时间值(旧的时间值(可能会被其它线程先一步设置,所以拿出来和 2 中的时间值比较))
            long currentExpireTime = Long.parseLong(redisService.getSet(key, String.valueOf(newExpireTime)));
            if(currentExpireTime == oldExpireTime) {
                return true;
            }
        }
        return false;
    }

/**
 * 解锁
 */
    public static void unLock2(String key) {    
        RedisService redisService = SpringUtils.getBean(RedisService.class);    
        long oldExpireTime = Long.parseLong(redisService.get(key, "0"));   
        if(oldExpireTime > System.currentTimeMillis()) {        
            redisService.del(key);    
        }
   }

 

  1. Zookeeper分布式锁又可以有两大类,这个暂时不做解释

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值