资讯专栏INFORMATION COLUMN

php并发控制中的独占锁

AZmake / 558人阅读

摘要:这中情况听过很多,在开发过程中也没刻意去模拟实验过。这就让两个以上的并发请求得到控制必须成功获取锁才能继续。

1.并发问题
并发大家都知道是什么情况,这里说的是并发多个请求抢占同一个资源,直接上实例吧

请求:index.php?mod=a&action=b&taskid=6
处理:

$key = "a_b::".$uid."_".$taskid;
$v = $redis->get($key);
if($v == 1){
    $redis->setex($key,10,1);
    //处理逻辑省略
}

2.分析

逻辑看来还可以,结果发现数据库中写入了两个同样的请求结果,我看了记录的时间戳,天!居然是同一秒.
我用microtime(true) log一下两个请求的时间差居然相差了0.0001s,就是说$redis->setex($key,10,1);还没执行成功 第二个请求已经get到跟第一个请求一样的结果。这不就是传说中的并发抢占资源。这中情况 听过很多,在开发过程中也没刻意去模拟实验过。

3.解决

方案1:第一反应就是要给处理过程加事务(数据库是mysql innoDB),加事务的结果就是 第一个请求成功了 第二个请求会执行到后面捡查发现重了会回滚。其实mysql事务在保证数据一致性上是很ok的,但是通过回滚来保证唯一资源独占代价太大,做过mysql事务测试测同学都知道,事务中的insert是已经插进去了,回滚之后才删掉的。

方案2:还有一个选择就是php中的文件独占锁,那就是说这情况下我要新建 用户数 * 任务数的文件来实现每个请求资源的独占,如果独占资源较少的话可选的解决办法:

    /**
     * 加锁
     */
    public function file_lock($filename){
        $fp_key = sha1($filename);
        $this->fps[$fp_key] = fopen($filename, "w+");
        if($this->fps[$fp_key]){
            return flock($this->fps[$fp_key], LOCK_EX|LOCK_NB);
        }
        return false;
    }
    /**
     * 解锁
     */
    public function file_unlock($filename){
        $fp_key = sha1($filename);
        if($this->fps[$fp_key] ){
            flock($this->fps[$fp_key] , LOCK_UN);
            fclose($this->fps[$fp_key] );
        }
    }

方案3:发现$redis->setnx()可以提供原子操作的状态:相同的key执行setnx之后没过期或者没del,再执行会返回false。这就让两个以上的并发请求得到控制必须成功获取锁才能继续。

    /**
     *  加锁
     */
    public function task_lock($taskid){
            $expire = 2;
             $lock_key ="task_get_reward_".$this->uid."_".$taskid;
            $lock = $this->redis->setNX($lock_key , time());//设当前时间
            if($lock){
                $this->redis->expire($lock_key,  $expire); //如果没执行完 2s锁失效
            }
            if(!$lock){//如果获取锁失败 检查时间
                $time = $this->redis->get($lock_key);
                if(time() - $time  >=  $expire){//添加时间戳判断为了避免expire执行失败导致死锁 当然可以用redis自带的事务来保证
                    $this->redis->rm($lock_key);
                }
                $lock =  $this->redis->setNX($lock_key , time());
                if($lock){
                    $this->redis->expire($lock_key,  $expire); //如果没执行完 2s锁失效
                }
            }
            return $lock;
        }
        /**
         *  解锁
         */
        public function task_unlock($taskid){
            $this->set_redis();
            $lock_key = "task_get_reward_".$this->uid."_".$taskid;
            $this->redis->rm($lock_key);
        }

说明下setNX 和expire 这两个操作其实可以用redis事务来保证一致性

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/21958.html

相关文章

  • php 并发控制中的独占

    并发大家都知道是什么情况,这里说的是并发多个请求抢占同一个资源,直接上实例吧 请求:index.php?mod=a&action=b&taskid=6处理: $key = a_b::.$uid._.$taskid; $v = $redis->get($key); if($v == 1){ $redis->setex($key,10,1); //处理逻辑省略 } 逻辑看来还可以,结果...

    yy13818512006 评论0 收藏0
  • 【java并发编程实战6】AQS之独占ReentrantLock实现

    摘要:锁与很好的隔离使用者与实现者所需要关注的领域。那么这个就是包装线程并且放入到队列的过程实现的方法。也证实了就是获取锁的线程的节点。如果发生异常取消请求,也就是将当前节点重队列中移除。 前言 自从JDK1.5后,jdk新增一个并发工具包java.util.concurrent,提供了一系列的并发工具类。而今天我们需要学习的是java.util.concurrent.lock也就是它下面的...

    sixleaves 评论0 收藏0
  • 深入浅出AQS之共享模式

    摘要:其二如果返回值等于表示当前线程获取共享锁成功,但它后续的线程是无法继续获取的,也就是不需要把它后面等待的节点唤醒。 在了解了AQS独占锁模式以后,接下来再来看看共享锁的实现原理。 原文地址:http://www.jianshu.com/p/1161... 搞清楚AQS独占锁的实现原理之后,再看共享锁的实现原理就会轻松很多。两种锁模式之间很多通用的地方本文只会简单说明一下,就不在赘述了,...

    Berwin 评论0 收藏0
  • mysql存储引擎

    摘要:存储格式数据索引结构分别存储于不同的文件中结构数据索引数据的存储顺序为,按插入顺序排列特定功能全文索引支持。支持数据压缩存储。 showImg(https://segmentfault.com/img/bVqzw6); 查看mysql支持的存储引擎列表 mysql> show enginesG; *************************** 1. row **********...

    callmewhy 评论0 收藏0
  • PHP Session并发操作阻塞问题

    摘要:前段时间的项目中遇到了并发操作阻塞问题,因为请求会使用存储数据。不过,这只限于来自同一个客户端的多个请求,也就是说,来自一个客户端的请求并不会阻塞另一个客户端的请求。这将大大增加页面的响应时间。至此解决并发阻塞问题。   前段时间的项目中遇到了session并发操作阻塞问题,因为请求会使用Session存储数据。   在同一个客户端浏览器打开不同标签进行测试的时候,请求一直处于等待状态...

    SnaiLiu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<