前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis 实现分布式锁

Redis 实现分布式锁

作者头像
陈大剩博客
发布2023-03-06 09:08:29
4480
发布2023-03-06 09:08:29
举报
文章被收录于专栏:陈大剩博客专栏

分布式锁介绍

分布式锁,主要考察使用者对原子性的理解,原子性可以保证程序从异常中恢复后,redis中的数据是正确的,程序依然正常运行。分布式锁是实现线程同步手段之一

分布式锁原理

分布式锁其实就是在进程里占一个“坑”,当别的进程来占坑时,发现那里已经有一根“大萝卜”了,就只好放弃或者稍后再试。占“坑”就是把所有的逻辑变成单进程来执行。

分布式锁原理
分布式锁原理

Redis 实现分布式锁

实现分布式锁,三个版本进行演变,最终达到完美的分布式锁功能。

基础版本
基础版本分布式锁
基础版本分布式锁
代码语言:javascript
复制
require_once("../RedisClient.php");
$client = RedisClient::getInstance();
/**
 * 基础版,完成锁的请求与释放,但是有严重的问题.
 * 如果请求锁完成后,宕机了,就成死锁了.再也不能请求到锁了
 * @return null
 */
function base () {
    global $client;
    //请求锁
    $lock = $client->set('lock','true');
    if(!$lock){
        return null;
    }

    // do something critical ...

    //释放锁
    $client->del('lock');
}
存在的问题

如果在释放锁前,服务器宕机了,那么我们永远都无法重新申请锁,就成死锁了。我们可以利用redis 的 set 命令给锁设置一个过期时间,服务器宕机了,锁过一段时间也会重新释放。

过期时间随机数版

过期时间随机数版
过期时间随机数版

很多同学会有疑问为什么需要添加随机数,不妨静下心仔细想想,如果我们给的时间过期了,锁不存在了,刚好另一个进程站用了这个“坑”,那我们是不是删错了呢?

代码语言:javascript
复制
require_once("../RedisClient.php");
$client = RedisClient::getInstance();
/**
 * 设置锁和添加过期时间放在一个命令中,要成功一起成功
 * 添加随机数,防止过期时间后,删除了其他进程的锁
 * @return null
 */
function perfect () {
    global $client;
    $unique = uniqid();
    //请求锁并添加过期时间
    $lock = $client->set('lock',$unique,'ex',60,'nx');
    if(!$lock){
        return null;
    }

    // do something critical ...

    //释放锁
    if ($client->get('lock')==$unique){
        $client->del('lock');
    };
}
存在的问题

由于 Redis 没有提供带条件删除的命令,需我们手动去匹配随机数,在进行了锁匹配中,有可能锁刚匹配完,本进程锁的过期时间到了,系统自动删除。而另一个进程已经申请了锁,我们命令删除另一个进程的锁,造成脏数据。这事我们需要用到 Lua 脚本进行原子性执行。

Lua 脚本版

代码语言:javascript
复制
use Predis\Command\ScriptCommand;
require_once("../RedisClient.php");
$client = RedisClient::getInstance();

class releaseLockScript extends ScriptCommand {

    public function getScript()
    {
        return <<<LUA
if redis.call("get",ARGV[1]) == ARGV[2] then
    return redis.call("del",ARGV[1])
else
    return 0
end
LUA;
    }
}
// 定义锁命令
$client->getProfile()->defineCommand('releaseLock','releaseLockScript');
function lua () {
    global $client;
    $unique = uniqid();
    //请求锁并添加过期时间
    $lock = $client->set('lock',$unique,'ex',60,'nx');
    if(!$lock){
        return null;
    }

    // do something critical ...

    //释放锁
    $client->releaseLock('lock',$unique)
}
存在的问题

当然这个也不是一个完美的方案,只是相对安全一点,如果真的超时了。其他逻辑还没有执行完,其他线程也会趁虚而入。

分布式锁的缺点

  • 分布式锁一旦加了之后,对同一个商品的下单请求,会导致所有客户端都必须对同一个商品的库存锁key进行加锁。这样会导致对同一个商品的下单请求,就必须串行化,一个接一个的处理。
  • 不适合执行较长逻辑的代码的请求。

拓展

分布式锁是一种思想,除了用 Redis 实现分布式锁外,也可以试着用:Mysql、Memcache、Zookeeper 等去实现

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 分布式锁介绍
  • 分布式锁原理
  • Redis 实现分布式锁
    • 基础版本
      • 存在的问题
        • 过期时间随机数版
          • 存在的问题
        • Lua 脚本版
          • 存在的问题
      • 分布式锁的缺点
      • 拓展
      相关产品与服务
      云数据库 Redis
      腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档