文档章节

Redis实现锁

秋风醉了
 秋风醉了
发布于 2016/04/14 16:17
字数 886
阅读 150
收藏 2

Redis实现锁

虽然很多的Redis用户都对锁(lock),加锁(locking)以及锁超时(lock timeouts)有所了解,但大部分使用Redis实现的锁只是基本上正确,它们发生故障的时间和方式通常都难以预料。下面列出了一些导致锁出现不正确行为的原因,以及锁在不正确运行时的症状:

  • 持有锁的进程因为操作时间过长而导致锁被自动释放,但进程本身并不知道,甚至还可能会错误的释放掉了其他进程持有的锁。

  • 一个持有锁并打算执行长时间操作的进程已经崩溃,但其他想要获取锁的进程不知道哪个进程持有着锁,只能白白浪费时间等待锁被释放

  • 在一个进程持有的锁过期之后,其他多个进程同时尝试去获取锁,并且都获得了锁。

  • 上面提到的第一个种情况和第三种情况同时出现,导致有多个进程获得了锁,而每个进程都以为自己是唯一一个获得锁的进程。

以上几种情况是redis锁使用过程中有可能出现的问题,在redis锁的使用过程中应该要注意。


Redis实现基本的加锁和锁的释放

为了对数据进行排他性访问,程序首先要做的就是获取锁。SETNX命令天生就适合用来实现锁的获取功能,这个命令只在键不存在的情况下为键设置值,而锁要做的就是将一个随机生成的128位UUID设置为键的值,并使用这个值来防止锁被其他进程取得。

如果程序在尝试获取锁的时候失败,那么它将不断的进行重试,知道成功的取得锁获取超时,代码如下,

/**
 * 获取锁
 *
 * @param conn
 * @param lockName
 * @param acquireTimeout 获取锁直到超时
 * @return
 */
public String acquireLock(Jedis conn, String lockName, long acquireTimeout) {
    String identifier = UUID.randomUUID().toString();

    long end = System.currentTimeMillis() + acquireTimeout;
    //获取锁的过程超时后,直接返回null
    while (System.currentTimeMillis() < end) {
        if (conn.setnx("lock:" + lockName, identifier) == 1) { //key-value设置成功
            return identifier; //返回锁的标识符
        }

        //如果没有设置成功,即获取锁失败,sleep一毫秒继续重试,直到获取锁的过程超时

        try {
            Thread.sleep(1);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

    return null;
}

以上代码实现了加锁过程,那么如何释放锁,

/**
 * 释放锁
 *
 * @param conn
 * @param lockName
 * @param identifier
 * @return
 */
public boolean releaseLock(Jedis conn, String lockName, String identifier) {
    String lockKey = "lock:" + lockName;

    while (true) {
        conn.watch(lockKey);
        //判断锁的标识,如果相等,说明标识符对应的线程正在持有该锁,可以释放
        if (identifier.equals(conn.get(lockKey))) {
            Transaction trans = conn.multi();
            trans.del(lockKey);
            List<Object> results = trans.exec();
            if (results == null) {
                continue;
            }
            return true;
        }

        //不再监视lockKey
        conn.unwatch();
        break;
    }

    //释放锁失败
    return false;
}


带有超时限制的锁

如下代码所示,

/**
 * 获取锁,锁有超时特性
 *
 * @param conn
 * @param lockName
 * @param acquireTimeout
 * @param lockTimeout
 * @return
 */
public String acquireLockWithTimeout(
        Jedis conn, String lockName, long acquireTimeout, long lockTimeout) {
    String identifier = UUID.randomUUID().toString();
    String lockKey = "lock:" + lockName;
    int lockExpire = (int) (lockTimeout / 1000);

    long end = System.currentTimeMillis() + acquireTimeout;

    //循环获取锁,直到获取成功或超时
    while (System.currentTimeMillis() < end) {
        if (conn.setnx(lockKey, identifier) == 1) { //设置成功
            //设置成功后设置锁的过期时间
            conn.expire(lockKey, lockExpire);
            return identifier;
        }

        //当获取锁失败后,检查过期时间
        //如果返回-1 表示该锁没有设置过期
        if (conn.ttl(lockKey) == -1) {
            //设置一个过期时间
            conn.expire(lockKey, lockExpire);
        }

        try {
            Thread.sleep(1);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }

    // null indicates that the lock was not acquired
    return null;
}

======END======

© 著作权归作者所有

共有 人打赏支持
上一篇: MongoDB入坑
下一篇: Redis事务
秋风醉了
粉丝 241
博文 566
码字总数 417505
作品 0
朝阳
程序员
私信 提问
基于Redis实现简单的分布式锁

摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问。分...

Float_Luuu
2016/05/18
1K
0
基于 Redis 实现简单的分布式锁

本文作者:伯乐在线 -Float_Lu 。未经作者许可,禁止转载! 欢迎加入伯乐在线专栏作者。 摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞...

伯乐在线
2016/05/21
0
0
Spring-data-redis + redis 分布式锁(二)

分布式锁的解决方式 基于数据库表做乐观锁,用于分布式锁。(适用于小并发) 使用memcached的add()方法,用于分布式锁。 使用memcached的cas()方法,用于分布式锁。(不常用) 使用redis的setnx...

xiaolyuh
2017/11/16
0
0
如何在springcloud分布式系统中实现分布式锁?

转载请标明出处: http://blog.csdn.net/forezp/article/details/68957681 本文出自方志朋的博客 最近在看分布式锁的资料,看了 Josial L的《Redis in Action》的分布式锁的章节。实现思路是...

forezp
2017/04/03
0
0
Redis分布式锁 基于GETSET SETNX REDISSON 的实现

Redis分布式锁的应用 有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等。大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多...

Lien6o
2017/09/21
26
0

没有更多内容

加载失败,请刷新页面

加载更多

推荐转载连接

https://www.cnblogs.com/ysocean/p/7409779.html#_label0

小橙子的曼曼
30分钟前
0
0
雷军亲自打造的套餐了解下:用多少付多少

12月28日消息,小米科技创始人兼CEO雷军微博表示,小米移动任我行套餐方案,原则上就是明明白白消费,用多少付多少,不用不花钱!上网、电话和短信都是一毛钱,上网0.1元/M,电话0.1元/分钟,...

linuxCool
42分钟前
0
0
协议简史:如何学习网络协议?

大学时,学到网络协议的7层模型时,老师教了大家一个顺口溜:物数网传会表应。并说这是重点,年年必考,5分的题目摆在这里,你们爱背不背。 考试的时候,果然遇到这个问题,搜索枯肠,只能想...

Java干货分享
51分钟前
5
0
雷军亲自打造的套餐了解下:用多少付多少

12月28日消息,小米科技创始人兼CEO雷军微博表示,小米移动任我行套餐方案,原则上就是明明白白消费,用多少付多少,不用不花钱!上网、电话和短信都是一毛钱,上网0.1元/M,电话0.1元/分钟,...

linux-tao
今天
2
0
在 Ubuntu 上为 CentOS 编译 Rust 程序

现在 CentOS 8 还没出来,最新的是 CentOS 7.6,上面搭载的 glibc 版本是 2.17,都已经是 2012 年那时候的版本了。 现在开发者比较常用的桌面 Linux 系统,比如 Ubuntu / Debian / Mint / A...

helloclia
今天
18
3

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部