在高并发买票数量异常问题

一、一般我们写的买票:查数量,如果有数量就卖出去,库存减一 (controller的sellTicket方法)

    //测试买票软件库存问题
    public function sellTicket()
    {
        $test = Db::name('test')->find(1);

        if($test['nums']>0){//买票
            $num = $test['nums']-1;
            $res = Db::name('test')->where('id', 1)->update(['nums'=>$num]);
            echo "买票成功,剩余数".$num;
        }else{
            echo "没有票了".$test['nums'];
        }
        
        //参考:https://blog.csdn.net/u011277123/article/details/78913649
        //测试apache的并发测试 ab工具:ab -n 200 -c 20 http://localhost/index.php (共执行200次并发是20)

    }

数据很简单如下(有100张票)

现在我们执行下并发在20共请求40次,的库存剩余是多少(当然剩余是60是我们的预期

=======果然不是我们要的结果,我们理想的答案是60,然而数据库剩余库存是63。

不难理解,有3个请求是同事发生的,它们读了相同的剩余库存,都减了1,写在了剩余量里面.

=======怎么避免这种情况呢?其实通用的方法有两种(一个数据库锁 [排它锁],另一个是文件锁)

二、我们用数据库锁来解决刚才的问题

    public function sellTicket()
    {
        //语法:LOCK TABLES t1 WRITE, t2 READ, ...; //加锁
        //Db::query("LOCK TABLES think_test WRITE");

        $test = Db::name('test')->lock(true)->find(1);//用了thinkphp里面带的锁
        if($test['nums']>0){//买票
            $num = $test['nums']-1;
            $res = Db::name('test')->where('id', 1)->update(['nums'=>$num]);
            echo "买票成功,剩余数".$num;
        }else{
            echo "没有票了".$test['nums'];
        }
        //UNLOCK TABLES; //去锁
        //Db::query("UNLOCK TABLES;");

        //参考:https://blog.csdn.net/u011277123/article/details/78913649
        //测试apache的并发测试 ab工具:ab -n 200 -c 20 http://localhost/index.php (共执行200次并发是20)

    }

结果是正确的:(100票进行40个请求20的并发测试)

三、我们来用文件锁,解决并发的问题

    //测试买票软件库存问题
    public function sellTicket()
    {
        $file = fopen(__DIR__.'/lock.txt','w+');
        if(flock($file,LOCK_EX)){
            //TODO 执行业务代码
            $test = Db::name('test')->find(1);//用了thinkphp里面带的锁
            if($test['nums']>0){//买票
                $num = $test['nums']-1;
                $res = Db::name('test')->where('id', 1)->update(['nums'=>$num]);
                echo "买票成功,剩余数".$num;
            }else{
                echo "没有票了".$test['nums'];
            }

            flock($file,LOCK_UN);//解锁
        }
        fclose($file);//关闭文件


        //参考:https://blog.csdn.net/u011277123/article/details/78913649
        //测试apache的并发测试 ab工具:ab -n 200 -c 20 http://localhost/index.php (共执行200次并发是20)

    }

通过命令 结果也是 预期结果.

数据库锁:https://blog.csdn.net/qq_35642036/article/details/89554721

文件锁参考:https://www.cnblogs.com/zhouguowei/p/9708380.html

原文地址:https://www.cnblogs.com/fps2tao/p/15162645.html