测试Redis的原子性及实现Redis的锁

4 篇文章 0 订阅

这几天都在学习Redis的相关知识,发现了一个问题,Redis虽然是单线程的,但是他有一个特点:IO多路复用,这样的特点使2个请求同时对同一key进行操作时,会出现2个请求同时拿到该key的值,进行了重复的操作,在秒杀中的体现为超卖;

具体代码为:

    public function redis1(){
        $redis = new \Redis();
        $redis->connect('127.0.0.1',6379);
        for ($i=1;$i<=500;$i++) {
            $num = $redis->get('val');
            $redis->set('val', $num+1);
            usleep(10000);
        }
    }

使用usleep是因为redis速度太快,不睡眠的话,体现不出来2个请求同时进行。。。

我发现了一个很奇怪的现象,如果我分别请求两次相同的接口,程序是同步进行的,第一个请求进行中的时候,第二个请求会阻塞。当两个接口都执行完毕,查看val的值是1000。(大概因为是在同一个浏览器执行同一个接口)

但我又写了一个一模一样的接口,redis2

    public function redis2(){
        $redis = new \Redis();
        $redis->connect('127.0.0.1',6379);
        for ($i=1;$i<=500;$i++) {
            $num = $redis->get('val');
            $redis->set('val', $num+1);
            usleep(10000);
        }
    }

我分别请求接口redis1和redis2,结果这两个请求是异步的(不同浏览器请求同一个接口也可以),所以也导致了最后val的值是800多,结果是小于1000的,这说明了两个请求在抢占资源,而且可能会同时抢占到同一个资源,然后进行操作。这就是为什么redis还是需要锁机制的原因了。。。

要使用redis的锁,我们需要用到的Redis方法是set,其中第三个参数是option,EX代表持续时间(防止死锁),NX表示如果key已经存在,则返回0,不存在则设值,并返回1。这个方法就很符合锁的特点,代码如下:

    public function redisTran1(){
        $redis = new \Redis();
        $redis->connect('127.0.0.1',6379);
        for ($i=1;$i<=500;$i++) {
            while (true){
                //拿锁的权限,没拿到则continue
                if(!$redis->set('lock', 1, ['EX'=>5, 'NX']))
                    continue;
                $redis->incr('val', 1);
                $redis->del('lock');//解锁
                break;
            }
            usleep(10000);
        }
    }

我们也是和上面一样,写一个一模一样的接口,redisTran2,然后分别请求接口redisTran1和redisTran2,最后的val的结果是1000,说明锁机制成功生效了。

2020-2-22 注

对于redis的原子性,我有了更加深入的理解,redis的原子性,说的是redis的api具有原子性,像第一次测试,val的值小于1000,这样的结果是不具备原子性的;事实上,redis的工作流程如下:

上面图片中的箭头都代表了redis的api,且它们是上一个执行完,下一个才继续执行,api是具有原子性的。

如果想要业务也具有原子性,直接用一步到位的方法,如incr。

此外,根据我的 调查,redis的事务是不具备原子性的,即前一条语句执行失败,后面的还是会继续执行。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值