基于Redis的分布式锁

来源:互联网 发布:php怎么安装 编辑:程序博客网 时间:2024/06/06 00:01

基于Redis的分布式锁

背景

主要最近在研究红包,抽奖,秒杀各类活动,考虑到多台服务器上,可能会获取到旧数据,所以上网查询了下方法,了解了分布式锁,一般是数据库的乐观锁或者用zookeeper或者reids做分布式锁,基本原理:用一个状态值表示锁,对锁的占用和释放通过状态值来标识。在这里做了下笔记。

基本的命令

1、SETNX(SET if Not eXists)
SETNX key value
将 key 的值设为 value ,当且仅当 key 不存在。
若给定的 key 已经存在,则 SETNX 不做任何动作。
返回值:
设置成功,返回 1 。
设置失败,返回 0 。
2、GET
GET key
返回 key 所关联的字符串值。
返回值:
当 key 不存在时,返回 nil ,否则,返回 key 的值。
如果 key 不是字符串类型,那么返回一个错误。
3、GETSET
GETSET key value
将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
返回值:
返回给定 key 的旧值。
当 key 没有旧值时,也即是, key 不存在时,返回 nil 。
4、DEL
DEL key [key …]
删除给定的一个或多个 key 。
返回值:
被删除 key 的数量。

实现(伪代码)

        if (SETNX(key, timestamp)) {            //获得锁,可以进行下步操作            return true;        } else {            //获取锁失败,需要先判断是否锁超时了,因为可能存在之前步骤获取锁后,超时宕机等情况,所以需要判断            currentTimestamp = get(key);            //如果其他线程设置了时间,这个判断不成立            if (currentTimestamp<System.currentTimeMillis(){                oldTimestamp = GETSET(key, nowTimestamp);                //获取上一个锁到期时间,并设置现在的锁到期时间,                //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的                if (oldTimestamp.equal(currentTimestamp)) {                    //获得锁,可以进行下步操作                    return true;                }            }            //可以继续循环,等待获取锁            return false;        }

问题

为什么前面的锁已经超时了,还要用getSet去设置新的时间戳的时间获取旧的值,然后和外面的判断超时时间的时间戳比较呢?
基于Redis的分布式锁

因为是分布式的环境下,可以在前一个锁失效的时候,有两个进程进入到锁超时的判断。如:
C0超时了,还持有锁,C1/C2同时请求进入了方法里面
C1/C2获取到了C0的超时时间
C1使用getSet方法
C2也执行了getSet方法
假如我们不加 oldValueStr.equals(currentValueStr) 的判断,将会C1/C2都将获得锁,加了之后,能保证C1和C2只能一个能获得锁,一个只能继续等待。

Reference:
1. http://blog.csdn.net/ugg/article/details/41894947/
2. http://www.cnblogs.com/0201zcr/p/5942748.html