分布式锁的实现方式有不少,本篇文章讲述一下使用Redis实现分布式锁。网上有不少使用Redis实现分布式锁的代码,可是这些代码或多或少都有问题。这篇文章会写一个实现,同时标明一些注意点。php
为了便于阐述,这里假设一个游戏场景,用户A有开山斧一把,价值500元宝,用户B有800元宝,想买A的开山斧,这些数据都存在Redis中。须要编写代码成功的实现该笔交易。html
Redis实现分布式锁,须要考虑以下问题:程序员
实现一个最低保障的分布式锁,须要具有三个特性redis
使用Redis实现分布式锁,通常使用SETNX或者SET命令,SETNX不能同时设置过时时间,若是使用的版本大于等于2.6.12,可使用SET命令,可使用这个命令原子性的实现SETNX和EXPIRE的功能,下面是两个命令的简介算法
命令格式:SETNX key value缓存
时间复杂度:O(1)安全
说明:将key
设置值为value
,若是key
不存在,这种状况下等同SET命令。 当key
存在时,什么也不作。SETNX
是”SET if Not eXists”的简写。网络
返回值框架
1
若是key被设置了0
若是key没有被设置命令格式:SET key value [EX seconds] [PX milliseconds] [NX|XX]编辑器
时间复杂度:O(1)
说明:将键key
设定为指定的“字符串”值。若是 key 已经保存了一个值,那么这个操做会直接覆盖原来的值,而且忽略原始类型。当set
命令执行成功以后,以前设置的过时时间都将失效。
选项
从2.6.12版本开始,redis为SET
命令增长了一系列选项:
EX
seconds – 设置键key的过时时间,单位时秒PX
milliseconds – 设置键key的过时时间,单位是毫秒NX
– 只有键key不存在的时候才会设置key的值XX
– 只有键key存在的时候才会设置key的值此处使用SETNX实现,毕竟有的公司Redis版本可能较低,使用SETNX能够实现,SET更加没有问题。
代码以下:
<?php
function uuid($prefix = '') {
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr($chars, 0, 8) . '-';
$uuid .= substr($chars, 8, 4) . '-';
$uuid .= substr($chars, 12, 4) . '-';
$uuid .= substr($chars, 16, 4) . '-';
$uuid .= substr($chars, 20, 12);
$ret = $prefix . $uuid;
return strtoupper($ret);
}
function acquireLock($redis,$lockName, $acquireTime = 10, $lockTime = 10) {
$lockKey = 'lock:' + $lockName;
$identifier = uuid('identify');
$end = time() + $acquireTime;
while (time() < $end) {
if ($redis->setnx($lockKey, $identifier)) {
$redis->expire($lockKey, $lockTime);
return $identifier;
} elseif ($redis->ttl($lockKey) == -1) {
$redis->expire($lockKey, $lockTime);
}
usleep(1000);
}
return false;
}
function process(){
$redis = new Redis();
$lockName = 'market';
//1.获取锁
$locked = acquireLock($redis,$lockName);
if($locked === false){
return false;
}
//2.进行交易
//判断A和B是否知足交易条件
//使用管道,对A和B进行操做
//3.释放锁
$releaseRes = releaseLock($redis,$lockName,$locked);
if($releaseRes === false){
return false;
}
}
function releaseLock($redis,$lockName,$identifier){
$lockKey = 'lock:' + $lockName;
$redis->watch($lockKey);
if($redis->get($lockKey) === $identifier){
$redis->multi();
$redis->del($lockKey);
$redis->exec();
return true;
}
$redis->unwatch();
return false;
}
复制代码
说明:
若是基于Redis单实例,假设这个单实例老是可用,这种方法已经足够安全。
但有两种特殊状况你们须要关注一下:
主从结构中存在明显的竞态:
在Redis的分布式环境中,有N个Redis master
这种状况可使用Redlock算法
本文讲述了怎样用Redis实现分布式锁,并写了具体实现和相关的分析。要用Redis实现分布式锁,有不少细节须要思考,你们能够根据本身的业务形态设计符合本身要求的锁,在复杂度和安全性上作好折中。
你们若是喜欢个人文章,能够关注个人公众号(程序员麻辣烫)
往期文章回顾: