使用redis实现分布式锁

标签: Redis 分布式锁
28人阅读 评论(0) 收藏 举报
分类:

分布式锁的应用场景

当多个机器(多个进程)会对同一条数据进行修改时,并且要求这个修改是原子性的。这里有两个限定:

  • 多个进程之间的竞争,意味着JDK自带的锁失效;
  • 原子性修改,意味着数据是有状态的,修改前后有依赖。

本文将先介绍Redis的实现方式,后面笔者会介绍分布式锁的其他实现。

在学习Redis实现分布式锁的过程中,笔者首先参考了Redis的官方文档实现RedLock。该文指出大部分的Redis分布式锁并没有考虑到Redis单点故障的问题并且文章指出即使搭建了Redis的集群,但基于节点之间异步模式来实现数据同步的过程中,也会导致两个进程获取同一个锁的问题。

笔者先介绍比较主流的Redis锁实现方法。如刚刚所述,该模式确实存在不少健壮性问题:

这里写图片描述

Redis的实现主要基于setnx 和给予一个超时时间(防止释放锁失败)。
多个尝试获取锁的客户端使用同一个key做为目标数据的唯一键,value为锁的期望超时时间点;
首先进行一次setnx命令,尝试获取锁,如果获取成功,则设置锁的最终超时时间(以防在当前进程获取锁后奔溃导致锁无法释放)

如果获取锁失败,则检查当前的锁是否超时,如果发现没有超时,则获取锁失败; 如果发现锁已经超时(即锁的超时时间小于等于当前时间),则再次尝试获取锁使用getset (修改为新值,获取旧值)

取到后判断下当前的超时时间和之前的超时时间是否相等,如果相等则说明当前的客户端是排队等待的线程里的第一个尝试获取锁的,让它获取成功即可。


    Jedis jedis;

    /**
     * 返回true表示获取锁成功
     **/
    public boolean lock(String resource, long expireTime) {
        long now = new Date().getTime();
        long lockExpireTime = now+expireTime;
        //isSuucess 为1表示设置成功。0表示未成功
        long isSuccess = jedis.setnx(resource, String.valueOf(lockExpireTime));
        //取锁成功,为key设置expire TTL
        if(isSuccess == 1) {
            jedis.expire(resource, expireTime);
            return true;
        }

        //取锁失败,继续流程检查当前的锁是否超时
        String lockExpireTimeInDB;
        try {
            lockExpireTimeInDB = jedis.get(resource);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return false;
        }

        //从redis get 失败
        if(lockExpireTimeInDB == null) {
          return false;
        }

        long oldExpireTime = Long.parseLong(lockExpireTimeInDB);

        //锁过期时间大于当前时间,锁没有超时,则获取锁失败
        if(oldExpireTime > now) {
            return false;
        }

        //锁过期时间小于当前时间,锁超时,则重新获取锁
        String  lockExpireTimeInDB2 = jedis.getSet(resource, String.valueOf(lockExpireTime));
        long oldExpireTime2 = Long.parseLong(lockExpireTimeInDB2);

        //相等,则取锁成功
        if(oldExpireTime != oldExpireTime2) {
              return false;
        }

        jedis.expire(resource, expireTime);
        return true;

    }

看完上述的代码,再来回顾下本文开头所说的问题,单点故障问题很好理解。那么即使搭建了Redis的集群,当进程1对master节点写入了锁,此时master节点宕机。slave节点提升为master而刚刚写入master的锁还未同步,此时进程2也将能够获取锁成功。此时必然会导致数据不同步问题。

Redis官方给出来一种解决方案RedLock,大致实现思路如下:
存在N个Redis服务(奇数个),之间完全独立没有构成集群。

当某个进程获取锁时,如果在N/2+1个Redis服务上成功写入了锁。则获取锁成功。如果获取锁失败,一定要再写入成功了的Redis服务上del

当释放锁时,再N个Redis服务上依次del

当一个客户端获取锁失败时,这个客户端应该在一个随机延时后进行重试,之所以采用随机延时是为了避免不同客户端同时重试导致谁都无法拿到锁的情况出现。

该实现可靠性确实提升了,但笔者认为该算法效率特别低。不适合生产环境。如果读者在生产环境中有使用Redis分布式锁的经验,欢迎留言介绍实现原理和处理容错方式。

查看评论

基于Redis实现简单的分布式锁

在分布式场景下,有很多种情况都需要实现最终一致性。在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务...
  • hupoling
  • hupoling
  • 2016-11-30 17:47:59
  • 2776

使用Redis实现分布式锁

1.实现分布式锁的几种方案     1.Redis实现   (推荐)     2.Zookeeper实现     3.数据库实现 Redis实现分布式锁 * * 在集群等多服务器中经常使用...
  • he90227
  • he90227
  • 2017-04-07 16:28:24
  • 4942

基于redis的分布式锁的实现

之前一直对分布式锁有所思考。一直觉得现在高性能的redis是个不错的选择; 关于分布式锁的思考 今天也尝试着写了一个基于redis的分布式锁工具 LockUtil.java package yyf...
  • qq_18860653
  • qq_18860653
  • 2017-02-07 10:51:09
  • 2357

使用redis和zookeeper实现分布式锁

redis和zookeeper实现分布式锁
  • haitunlianren
  • haitunlianren
  • 2017-07-01 14:18:21
  • 542

分布式锁的作用及实现(Redis)

一、什么是分布式锁?要介绍分布式锁,首先要提到与分布式锁相对应的是线程锁、进程锁。线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一...
  • L_BestCoder
  • L_BestCoder
  • 2018-02-19 15:29:38
  • 1474

java结合redis实现分布式锁

redis 分布式锁 java
  • u011322089
  • u011322089
  • 2017-03-16 19:34:43
  • 3978

REDIS 学习(10)流程图解使用redis实现分布式锁

redis作为集中式缓存,可以作为分布式锁来使用。 首先用到的reidis操作有: setnx key value  当key不存在的时候生效并返回1,当已经有此key的时候返回0 getset ke...
  • kkgbn
  • kkgbn
  • 2016-09-28 17:23:23
  • 1480

redis、zookeeper分布式锁的实现

redis、zookeeper分布式锁的实现
  • u013673242
  • u013673242
  • 2017-07-12 16:15:51
  • 624

Redis实现分布式锁原理与实现分析

一、关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到。  我举二个例子:  场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子...
  • jp413670706
  • jp413670706
  • 2016-10-05 08:02:04
  • 6946

基于Redis实现的分布式锁

基于Redis的分布式锁实现背景 根据redis的setnx命令实现只有一个客户端可以拿到锁; RedissonLock的分布式锁实现使用了lua脚本,这里提供一种不适用脚本实现的方法; 基本实现 使...
  • ljinshuan
  • ljinshuan
  • 2017-05-14 16:22:14
  • 569
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 67万+
    积分: 7827
    排名: 3360
    博客专栏
    最新评论