以下是我在工作中用到的类,redis加锁两种方式,解锁为了保证原子性所以只用lua+redis的方式

缺陷:虽然死锁问题解决了,但业务执行时间超过锁有效期还是存在多客户端加锁问题。
不过,这个类已经满足了我现在的业务需求

更优的解决方案可以参考以下两篇文章:
https://redis.io/topics/distlock (Redlock的算法描述)
https://mp.weixin.qq.com/s/1bPLk_VZhZ0QYNZS8LkviA

代码实现:

class RedisLock
{
/**
* @var 当前锁标识,用于解锁
*/
private $_lockFlag;

private $_redis;

public function __construct($host = '127.0.0.1', $port = '6379', $passwd = '')
{
$this->_redis = new Redis();
$this->_redis->connect($host, $port);
if ($passwd) {
$this->_redis->auth($passwd);
}
}

public function lock($key, $expire = 5)
{
$now= time();
$expireTime = $expire + $now;
if ($this->_redis->setnx($key, $expireTime)) {
$this->_lockFlag = $expireTime;
return true;
}

// 获取上一个锁的到期时间
$currentLockTime = $this->_redis->get($key);
if ($currentLockTime < $now) {
/* 用于解决
C0超时了,还持有锁,加入C1/C2/...同时请求进入了方法里面
C1/C2都执行了getset方法(由于getset方法的原子性,
所以两个请求返回的值必定不相等保证了C1/C2只有一个获取了锁) */
$oldLockTime = $this->_redis->getset($key, $expireTime);
if ($currentLockTime == $oldLockTime) {
$this->_lockFlag = $expireTime;
return true;
}
}

return false;
}

public function lockByLua($key, $expire = 5)
{
$script = <<<EOF

local key = KEYS[1]
local value = ARGV[1]
local ttl = ARGV[2]

if (redis.call('setnx', key, value) == 1) then
return redis.call('expire', key, ttl)
elseif (redis.call('ttl', key) == -1) then
return redis.call('expire', key, ttl)
end

return 0
EOF;

$this->_lockFlag = md5(microtime(true));
return $this->_eval($script, [$key, $this->_lockFlag, $expire]);
}

public function unlock($key)
{
$script = <<<EOF

local key = KEYS[1]
local value = ARGV[1]

if (redis.call('exists', key) == 1 and redis.call('get', key) == value)
then
return redis.call('del', key)
end

return 0

EOF;

if ($this->_lockFlag) {
return $this->_eval($script, [$key, $this->_lockFlag]);
}
}

private function _eval($script, array $params, $keyNum = 1)
{
$hash = $this->_redis->script('load', $script);
return $this->_redis->evalSha($hash, $params, $keyNum);
}

}

$redisLock = new RedisLock();

$key = 'lock';
if ($redisLock->lockByLua($key)) {
// to do...
$redisLock->unlock($key);
}

php+redis+lua实现分布式锁(转载)的更多相关文章

  1. Redis系列(二)--分布式锁、分布式ID简单实现及思路

    分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...

  2. 如何用redis正确实现分布式锁?

    先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...

  3. redis系列:分布式锁

    redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...

  4. Redis如何实现分布式锁

    今天我们来聊一聊分布式锁的那些事. 相信大家对锁已经不陌生了,我们在多线程环境中,如果需要对同一个资源进行操作,为了避免数据不一致,我们需要在操作共享资源之前进行加锁操作.在计算机科学中,锁(lock ...

  5. 基于redis实现的分布式锁

    基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...

  6. 一个Redis实现的分布式锁

    import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.conne ...

  7. 基于Redis的简单分布式锁的原理

    参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...

  8. redis客户端、分布式锁及数据一致性

    Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...

  9. 在redis上实现分布式锁

    /** *在redis上实现分布式锁 */ class RedisLock { private $redisString; private $lockedNames = []; public func ...

随机推荐

  1. linux python3安装whl包时报错解决:is not a supported wheel on this platform

    原因1 你下载安装的包不是当前平台所支持的 原因2 你下载的包,不符合你所在的平台的安装whl的名称规范,所以出错.比如当前我要安装的包是:pymssql-2.1.5-cp36-cp36m-manyl ...

  2. VMWare虚拟机显示模块“Disk”启动失败

    找到启动虚拟机的目录: 在此路径中找到.vmx文件,在文件中查找(Ctrl+F快速查找)vmci0.present,此时会看到"vmci0.present = "TRUE" ...

  3. 【SpringBoot基础系列】手把手实现国际化支持实例开发

    [SpringBoot基础系列]手把手实现国际化支持实例开发 国际化的支持,对于app开发的小伙伴来说应该比价常见了:作为java后端的小伙伴,一般来讲接触国际化的机会不太多,毕竟业务开展到海外的企业 ...

  4. Vue之前后端交互

    Vue之前后端交互 一.前后端交互模式 接口调用方式 原生ajax 基于jQuery的ajax fetch axios 异步 JavaScript的执行环境是「单线程」 所谓单线程,是指JS引擎中负责 ...

  5. mysql-redis连接

    # log 数据库连接 class LogMysql(object): conn = None cursor = None def __init__(self): self.conn = pymysq ...

  6. 高并发Flask服务部署

    高并发Flask服务部署 AI模型持久化 OOP: 利用面向对象思想,实现算法在内存上的实例化及持久化.即一次模型加载,多次请求调用. class ocr_infer_class(threading. ...

  7. nvGRAPH API参考分析(二)

    nvGRAPH API参考分析(二) nvGRAPH Code Examples 本文提供了简单的示例. 1. nvGRAPH convert topology example void check( ...

  8. 实用的jar包加密方案

    前言 jar包相信大家都很熟悉,是通过打包java工程而获得的产物,但是jar包是有一个致命的缺点的,那就是很容易被反编译,只需要使用jd-gui就可以很容易的获取到java源码. 如果你想要防止别人 ...

  9. 开发掉坑(一)tar命令解压文件覆盖源文件

    今天在编译机上编译前端代码,报了找不到依赖的异常.检查后发现是node_modules/.bin下少了一些文件. 一开始疑惑为什么本地能成功生成软链在node_modules/.bin,服务器上面却不 ...

  10. Android系统编程入门系列之应用环境及开发环境介绍

        作为移动端操作系统,目前最新的Android 11.0已经发展的比较完善了,现在也到了系统的整理一番的时间,接下来的系列文章将以Android开发者为中心,争取用归纳总结的态度对初级入门者所应 ...