秒杀商品的一些思路分享,redis的链表、mq、mysql排他锁

本文介绍了两种秒杀场景下的技术解决方案:1) 使用MySQL事务和排他锁,通过TP5.1框架实现库存减1并创建订单;2) 结合Redis链表和RabbitMQ,利用链表预库存特性确保不超卖,并通过消息队列处理订单创建。推荐使用Redis链表+Mq方案,以提高并发处理能力和系统稳定性。
摘要由CSDN通过智能技术生成

运用的技术

1、redis链表(预库存)

2、rabbitMq(记录秒杀成功的订单数据)

3、php

 

两个方案

1、mysql 开启事务,生成排他锁🔒

2、redis链表+Mq (推荐)

 

方案一:mysql开启事务,生成排他锁

tp5.1框架代码

了解tp5.1的两个数据库方法
 

setInc():将数字字段值增加

setDec():将数字字段值减少

通过事务实现排他锁,可以防止超卖

这里是先修改商品库存 -1 后再添加到订单表 

他的流程就是以下:

秒杀请求 -> 启动事务 -> 修改商品 -> 生成排他锁 -> 创建订单 -> 提交事务 -> 释放排他锁 -> 处理下一个秒杀请求

public function order(Request $request)
    {
        // 商品id
        $id = $request->param('id');  
        // 模拟购买用户的手机号
        $mobile = '13888888888';
        //  实际业务处理
        try {
            // 启动事务
            Db::startTrans();
            // 下面setDec 会产生排他锁,10个并发过来,这里会产生排队的,库存-1
            $res = Db::name('goods')->where('id', $id)->setDec('num');
            // 另外知识:如果把上面那句注释掉,这里就是共享锁了,10个并发,他们可能读到同一个数据
            // 所以这里必须要先修改后查询,才会有排他锁
            $num = Db::name('goods')->where('id', $id)->value('num');

            if ($num < 0) {
                return json(['status' => 'fail', 'msg' => '秒杀失败']);
            }
            if ($res) {
                // 秒杀成功的用户,开始存进订单表   
                Db::name('orders')->insert(['mobile' => $mobile, 'goods_id' => $id]);
                // 提交事务,释放排他锁
                Db::commit();
                return json(['status' => 'success', 'msg' => '秒杀成功']);
            }

        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
        }
    }

方案二:redis链表+Mq (推荐)

1、利用链表的特性,先把商品存进链表,因为链表是会取出来后就不会存在的了,可以利用这个特性来做个预库存

2、等获取链表数据为空的时候,就代表秒杀失败了

3、获取到数据,也把用户的顺序记录进另一条链表中,后续再添加进订单,也可以减少这一步操作,看情况而定

首先我们来看看链表的特性

 

lpush goods 商品id1 模拟商品的id为1的库存 有5个

127.0.0.1:6379> lpush goods 商品id_1
(integer) 1
127.0.0.1:6379> lpush goods 商品id_1
(integer) 2
127.0.0.1:6379> lpush goods 商品id_1
(integer) 3
127.0.0.1:6379> lpush goods 商品id_1
(integer) 4
127.0.0.1:6379> lpush goods 商品id_1
(integer) 5
127.0.0.1:6379> lrange goods 0 -1
1) "\xe5\x95\x86\xe5\x93\x81id_1"
2) "\xe5\x95\x86\xe5\x93\x81id_1"
3) "\xe5\x95\x86\xe5\x93\x81id_1"
4) "\xe5\x95\x86\xe5\x93\x81id_1"
5) "\xe5\x95\x86\xe5\x93\x81id_1"
127.0.0.1:6379>

如果这时候有人秒杀成功了,就会在队列中取出一个
lpop 删除左边第一个节点,并将其返回,可以看到,现在还剩四个,那么就可以保证了不会超卖

127.0.0.1:6379> lpop goods
"\xe5\x95\x86\xe5\x93\x81id_1"
127.0.0.1:6379> lrange goods 0 -1
1) "\xe5\x95\x86\xe5\x93\x81id_1"
2) "\xe5\x95\x86\xe5\x93\x81id_1"
3) "\xe5\x95\x86\xe5\x93\x81id_1"
4) "\xe5\x95\x86\xe5\x93\x81id_1"
127.0.0.1:6379>

当lpop到没有数据的时候,就提示秒杀已经没有库存了。

 

那么现在开始上代码;他的流程以下:

秒杀来了->通过商品id获取链表中的商品id->这里是生成订单数据并且推送mq和存储redis链表的,但这里只是说思路,所以就通过商品id+手机号存储进链表 && 推送mq -> 消费者(mq消费者可以多个)开始消费订单数据


    public function order(Request $request)
    {
        
        $mobile = $request->input('mobile');
        $goods_id = $request->input('goods_id');
        // 从链表中取出来
        $goods_id = $this->redis->lpop('这里是商品id');

        if ($goods_id) {
            // 之所以在redis也存储,是为了避免mq生产失败,还有个备用方案
            $this->redis->lpush('orders', $mobile . '==' . $goods_id);
            // 存放抢购成功的用户到mq中,然后消费者就是负责生成订单(同步到mysql)的,生成订单之后就去支付
            // 我问了老师,说如果有人支付失败,或者取消订单的,可以后续随便在秒杀订单中找一笔来让用户支付。
            // 他说但是一般情况下不会有以上情况的,因为这样就不是秒杀了。可以参考京东的秒杀。
            $this->sendOrder(json_encode(['mobile' => $mobile, 'goods_id' => $goods_id]));

            return json(['status' => 'success', 'msg' => '秒杀成功']);
        } else {
            return json(['status' => 'fail', 'msg' => '秒杀失败']);
        }

    }

    public function sendOrder($data)
    {
        $connection = new AMQPStreamConnection(config('rabbitmq.rabbit-server'), config('rabbitmq.rabbit-port'), config('rabbitmq.rabbit-user'), config('rabbitmq.rabbit-password'));
        $channel = $connection->channel();

        $queue_name = config('rabbitmq.rabbit-queue');

        $channel->queue_declare($queue_name, false, false, false, false);

        $msg = new AMQPMessage($data);
        $channel->basic_publish($msg, '', $queue_name);


        $channel->close();
        $connection->close();
    }

如果后续有人退款,或者支付失败的,那么就可以相应的在redis的链表中,加上商品预库存即可,商品预库存就是我刚刚lpush那样子添加即可。

 

大概的秒杀思路就是如此。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值