zoukankan      html  css  js  c++  java
  • Redis锁的基本应用

    处理高并发问题时,我们经常用 Redis 进行加锁操作,目的是为了解决并发可能带来的问题。做一个简单的总结

    常见的方案之一:setnx,其他线程必须拿到这个值,才能继续往下执行,否则等待。该命令是原子操作,所以可以防止并发情况的发生。

    while(!$redis->setnx('lock', '1')) {    // 设置锁
        usleep(100000);
    }
    
    // 执行业务代码
    
    $redis->del('lock');    // 释放锁

    但是该方案有个弊端,如果设置锁后进程崩溃,那么该锁永远不会释放。一般解决方法是在 setnx 的时候设置过期时间,则可以解决线程奔溃锁无法释放的问题。但如果设置锁和设置锁的过期时间不是原子操作,仍然不能防止并发的情况发生。好在 Redis 中的 set 命令提供了这些参数使用,NX 就和 setnx 一样, EX 可以在 set 的时候加上过期时间。

    $expire = 5;    // 锁的过期时间
    while(!$redis->set('lock', '1', ['NX', 'EX'=>$expire])) {
        usleep(100000);
    }
    
    // 执行业务代码
    
    $redis->del('lock');

    但是在这种情况下,当一个进程执行出现问题或执行时间超过 setnx 设置的过期时间,那么这个锁就自动消失了,仍会有其他进程并发执行业务代码,且不同进程的锁相互覆盖。所以这个方案也不能有效防止并发。

    解决方法: watch,在 set 的时候设置 NX 与 EX,并且设置值为随机数(唯一),当 A 进程设置锁后,后续进程都无法设置锁。A 进程业务逻辑完成后,对比随机数是否一致,如果一致再删除,如果在删除过程中,发现 key 的值被修改,则删除失败。防止 A 进程超时后,锁被后续进程获取,这个时候如果 A 进程删除锁,就会把后面的锁给删了。

    $expire = 5;
    $random = session_create_id();  
    while(!$redis->set('lock', $random, ['NX', 'EX'=>$expire])) {
        usleep(100000);
    }
    $redis->watch("lock");  // 监听 lock 的值
    
    // 执行业务代码
    
    $ret = $redis->multi(Redis::MULTI)
        ->del("lock")
        ->exec();   // multi 的作用在于若被 watch 的键被修改、删除、覆盖,那么 exec 就会执行失败
    if($ret) {
        $redis->del("lock"); // 释放锁
    } else {
        // 代表 lock 的值已被其他进程修改
    }
  • 相关阅读:
    解决取消input标签中disabled属性
    【转】jQuery源码分析-13 CSS操作-CSS-类样式-addClass+removeClass+toggleClass+hasClass
    Bigpipe学习【转】
    用户请求的生命周期[传统模式与BigPipe]
    gcc扩展语法一:在表达式中的语句和声明
    分享一个关于pthread线程栈在mm_struct里面的分布问题
    neo4j 安装步骤 转自:http://blog.csdn.net/luoluowushengmimi/article/details/19987995
    sudoers文件解析 转自:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=1971013
    匿名union
    VMware虚拟机修改BIOS启动项
  • 原文地址:https://www.cnblogs.com/tangxuliang/p/10672276.html
Copyright © 2011-2022 走看看