使用laravel解决库存超出的几个方案

数据库字段

使用laravel解决库存超出的几个方案

1.错误的示范

    /**
     * 错误示范
     * Create by Peter Yang
     * 2021-06-08 10:57:59
     * @return string
     */
    function test1()
    {

        //商品id
        $id = request()->input('id');

        $product = Product::where('id', $id)->firstOrFail();

        if ($product->num <= 0) {

            return "卖光啦!!";
        }

        //库存减1
        $product->decrement('num');

        return "success";

    }

使用go模拟并发

package main

import (
    "fmt"
    "github.com/PeterYangs/tools/http"
    "sync"
)

func main() {

    client := http.Client()

    wait := sync.WaitGroup{}

    for i := 0; i < 50; i++ {

        wait.Add(1)

        go func(w *sync.WaitGroup) {

            defer w.Done()

            res, _ := client.Request().GetToString("http://www.api/test1?id=1")

            fmt.Println(res)

        }(&wait)

    }

    wait.Wait()

}

在数据库中查看库存

使用laravel解决库存超出的几个方案
库存已超出

2.redis原子锁

    /**
     * redis原子锁
     * Create by Peter Yang
     * 2021-06-08 11:00:31
     */
    function test2()
    {
        //商品id
        $id = request()->input('id');

        $lock = \Cache::lock("product_" . $id, 10);

        try {

            //最多等待5秒,5秒后未获取到锁,则抛出异常
            $lock->block(5);

            $product = Product::where('id', $id)->firstOrFail();

            if ($product->num <= 0) {

                return "卖光啦!!";
            }
            //库存减1
            $product->decrement('num');

            return 'success';

        }catch (LockTimeoutException $e) {

            return '当前人数过多';

        } finally {

            optional($lock)->release();
        }
    }

库存正常

使用laravel解决库存超出的几个方案

3.mysql悲观锁

    /**
     * mysql悲观锁
     * Create by Peter Yang
     * 2021-06-08 11:00:47
     */
    function test3()
    {

        //商品id
        $id = request()->input('id');

        try {
            \DB::beginTransaction();
            $product = Product::where('id', $id)->lockForUpdate()->first();

            if ($product->num <= 0) {

                return "卖光啦!!";
            }

            //库存减1
            $product->decrement('num');

            \DB::commit();

            return "success";

        } catch (\Exception $exception) {

        }

    }

库存正常

使用laravel解决库存超出的几个方案

4.mysql乐观锁

    /**
     * mysql乐观锁
     * Create by Peter Yang
     * 2021-06-08 11:00:47
     */
    function test4()
    {

        //商品id
        $id = request()->input('id');

        $product = Product::where('id', $id)->first();

        if ($product->num <= 0) {

            return "卖光啦!!";
        }

        //修改前检查库存和之前是否一致,不一致说明已经有变动,则放弃更改
        $res = \DB::update('UPDATE `product` SET num = num -1 WHERE id = ? AND num=?', [$id, $product->num]);

        if (!$res) {

            return '当前人数过多';

        }

        return 'success';


    }

库存正常

使用laravel解决库存超出的几个方案

优化乐观锁

修改库存的sql修改为

\DB::update('UPDATE `product` SET num = num -1 WHERE id = ? AND num-1 >= 0', [$id]);
本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 4天前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 13

项目上遇到这样的问题,刚好看到你的文章,对我很有帮助,谢谢~

5天前 评论

现在很需要用 大佬

5天前 评论

应该加一个把数量 push 到 redislist 上, 这样子就不用加锁了.

5天前 评论
一位不愿意透露姓名的杨先生 (楼主) 5天前
seth-shi (作者) 4天前
// UPDATE `product` SET num = num -1 WHERE id = ? AND num-1 >= 0
UPDATE `product` SET num = num -1 WHERE id = ? AND num >= 1
5天前 评论

把数据库设置成无符号

5天前 评论

偏个题,PHP 作为一门图灵完备语言,模拟并发完全可以自己语言内解决,可以看这篇 数据库不使用悲观锁导致问题的一种复现方式

4天前 评论
一位不愿意透露姓名的杨先生 (楼主) 4天前
tu6ge-php (作者) 4天前
seth-shi 4天前
Product::query()->where('id', $id)->where('num', '>=', 1)->decrement('num');
4天前 评论
我爱大可乐

如果用悲观锁的话,当并发量大的时候,虽然数据会保证准确性,但是会造成堵塞、响应会变慢,体验不好。

4天前 评论

库存字段无符号unsigned,不就少了个num>0的条件吗

4天前 评论
我爱大可乐

如果使用乐观锁或者悲观锁,虽然能保证数量正确,但是会有大量的请求返回 当前人数过多 。测试请求数量100009896 个返回 当前人数过多。测试使用的是 memcached 数量没有啥问题,redis 还没有测试。建议如果并发量大的话不要使用悲观锁或者乐观锁。

3天前 评论

写的很好,不过真实环境并不可取,经历过大的并发,这种方案给mysql带来太大的负担了
如果我库存只相信redis 呢?
就意味着我每次只需要去操作redis 即可. 并且一直自减即可,库存完美解决
这种秒杀的场景尽量少关注数据库库存的东西,因为这里的数据库除了锁真的不可信,但是用锁的话,维护的东西要更多
库存真正扣除的时间是用户付款成功了, 或者说下单成功了(还没没支付呢), 当订单被取消或者是订单超时被动取消的时候库存回滚的时候也很简单 自增回去就好.
不是一直都要及时获取答案,因为正确的不一定要快,但是一定要保证正确

7小时前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!