博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
php 并发控制中的独占锁
阅读量:6573 次
发布时间:2019-06-24

本文共 2317 字,大约阅读时间需要 7 分钟。

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

请求: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);    //处理逻辑省略}

逻辑看来还可以,结果发现数据库中写入了两个同样的请求结果,我看了记录的时间戳,天!居然是同一秒.

我用microtime(true) log一下两个请求的时间差居然相差了0.0001s,就是说$redis->setex($key,10,1);还没执行成功 第二个请求已经get到跟第一个请求一样的结果。这不就是传说中的并发抢占资源。这中情况 听过很多,在开发过程中也没刻意去模拟实验过
第一反应就是要给处理过程加事务(数据库是mysql innoDB),加事务的结果就是 第一个请求成功了 第二个请求会执行到后面捡查发现重了会回滚
其实mysql事务在保证数据一致性上是很ok的,但是通过回滚来保证唯一资源独占代价太大,做过mysql事务测试测同学都知道,事务中的insert是已经插进去了,回滚之后才删掉的。
还有一个选择就是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] );        }    }

发现$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事务来保证一致性

转载地址:http://vggjo.baihongyu.com/

你可能感兴趣的文章
hihocoder 1014 Trie树
查看>>
ADO.NET笔记——使用DataSet返回数据
查看>>
MOTO XT702添加开机音乐
查看>>
Python脚本日志系统
查看>>
B0BO TFS 安装指南(转载)
查看>>
gulp常用命令
查看>>
TCP(Socket基础编程)
查看>>
RowSet的使用
查看>>
每日一记--cookie
查看>>
WPF and Silverlight 学习笔记(十二):WPF Panel内容模型、Decorator内容模型及其他...
查看>>
MySQL:创建、修改和删除表
查看>>
Java多线程程序设计详细解析
查看>>
IOS 7 Study - UISegmentedControl
查看>>
八、通用类型系统
查看>>
JQuery的ajaxFileUpload的使用
查看>>
Java分享笔记:使用keySet方法获取Map集合中的元素
查看>>
Java面向对象练习题之人员信息
查看>>
关于Integer类中parseInt()和valueOf()方法的区别以及int和String类性的转换.以及String类valueOf()方法...
查看>>
ios 控制器的生命周期
查看>>
C#动态代理
查看>>