zoukankan      html  css  js  c++  java
  • 面试题--秒杀系统引发的思考

    看到这个面试题的时候,脑海中立马浮现出“队列”,那就先来解释下队列吧~

    队列和堆栈一样,都是常用的数据结构,特点是先进先出

    参考:https://www.cnblogs.com/be-thebest/p/9983672.html

    场景说明:

    1.初始化队列时,生成一个队列,传入一个参数作为maxsize初始化队列把队尾rear设为0,队头front也设为0,此时queue中只有0号元素,并且rear和front都指向它。

    2.入队时,先需要判断队列是否已满(front-rear == maxsize),如果已满不可在插入,如果未满则允许插入。插入时,front自增,然后依次让队列所有元素向前移动一位(让出队尾位置以便插入新元素),然后生成新的data对象插入到队尾位置。

    3.出队时,判断队列是否为空(front == rear),如果为空时,无法出队。如果不为空时,删除front指向的对象,并且front自减,完成出队。

    实现代码:

    <?php
    
    class data {
        //数据
        private $data;
    
        public function __construct($data) {
            $this->data = $data;
            echo $data . ":哥进队了!<br>".PHP_EOL;
        }
    
        public function getData() {
            return $this->data;
        }
    
        public function __destruct() {
            echo $this->data . ":哥走了!<br>".PHP_EOL;
        }
    }
    
    class queue {
        protected $front;//队头
        protected $rear;//队尾
        protected $queue = array('0' => '队尾');//存储队列
        protected $maxsize;//最大数
    
        public function __construct($size) {
            $this->initQ($size);
        }
    
        //初始化队列
        private function initQ($size) {
            $this->front = 0;
            $this->rear = 0;
            $this->maxsize = $size;
        }
    
        //判断队空
        public function QIsEmpty() {
            return $this->front == $this->rear;
        }
    
        //判断队满
        public function QIsFull() {
            return ($this->front - $this->rear) == $this->maxsize;
        }
    
        //获取队首数据
        public function getFrontDate() {
            return $this->queue[$this->front]->getData();
        }
    
        //入队
        public function InQ($data) {
            if ($this->QIsFull()) echo $data . ":我一来咋就满了!(队满不能入队,请等待!)".PHP_EOL;
            else {
                $this->front++;
                for ($i = $this->front; $i > $this->rear; $i--) {
                    //echo $data;
                    if (isset($this->queue[$i])) {
                        unset($this->queue[$i]);
                    }
                    $this->queue[$i] = $this->queue[$i - 1];
                }
                $this->queue[$this->rear + 1] = new data($data);
              print_r($this->queue);
                //echo $this->front;
                echo '入队成功!<br>'.PHP_EOL;
            }
        }
    
        //出队
        public function OutQ() {
            if ($this->QIsEmpty()) echo "队空不能出队!<br>".PHP_EOL;
            else {
                unset($this->queue[$this->front]);
                $this->front--;
                //print_r($this->queue);
                //echo $this->front;
                echo "出队成功!<br>".PHP_EOL;
            }
        }
    }
    
    $q = new queue(3);
    $q->InQ("小苗");
    $q->InQ('马帅');
    $q->InQ('溜冰');
    $q->InQ('张世佳');
    $q->OutQ();
    $q->InQ("周瑞晓");
    $q->OutQ();
    $q->OutQ();
    $q->OutQ();
    $q->OutQ();
    View Code

    方案1:数据库

    悲观锁:增删改查都上锁

    乐观锁:大约等于无锁

    排他锁:配合事务用,commit后才等于解锁(暂时没发现和悲观锁有什么区别,日后在补充)

    1:开启事务
    2:查询库存,并显示的设置写锁(排他锁):SELECT * FROM goods WHERE id = 1 FOR UPDATE
    3:生成订单
    4:去库存,隐示的设置写锁(排他锁):UPDATE goods SET counts = counts – 1 WHERE id = 1
    5:commit,释放锁

    方案2:分布式锁(Memcached、Redis)

    用redis来做一个分布式锁,reids->setnx('lock', 1) 设置一个锁,程序执行完成再del这个锁。

    锁定的过程,不利于并发执行,大家都在等待锁解开,不建议使用。

    方案3:消息队列(RabbidMQ, ActiveMQ,Kafka)

    将订单请求全部放入消息队列,然后另外一个后台程序一个个处理队列中的订单请求。

    并发不受影响,但是用户等待的时间较长,进入队列的订单也会很多,体验上并不好,也不建议使用。

    • 消息队列(RabbidMQ, ActiveMQ,Kafka)主要有两种使用模式:生产者->消费者,发布者->订阅者第一种方式是一对一,后者是一对多消费掉,队例中数据便不再保存,所谓阅后即焚。消息队列的存和取一般是不同的服务,用于服务之间的异步操作,解耦同步操作。
    • Redis属于NoSQL类型的缓存/存储,侧重点在支持快速反复读取。数据的产生和消费可能是同一个服务,也可以是多个,如集群。

    方案4: redis递减

    代码设计

      1、当用户开始参与秒杀时,将秒杀程序的请求写入Redis(uid, time)中。

      2、假如只有10个人可以秒杀成功,检查Redis已经存放数据的长度,达到上限后不再插入,说明秒杀已完成。

      3、最后循环处理存入Redis中的10条数据,然后慢慢取数据存入到数据库中。

    在秒杀这一块对数据库压力特别大,如果我们直接在用户发起秒杀请求时,每次都查询数据库是否已经达到秒杀人数上限的话,会造成数据库压力巨大。现在通过Redis的一个队列list,然后把秒杀请求放入到Redis里面,最后将秒杀成功的数据通过入库程序写入到数据库,这样的话会极大缓解mysql的压力。

    redis数据类型中的list类型

      redis 的list 是一个双向链表,可以从头部或者尾部追加数据。

      * LPUSH/LPUSHX :将值插入到(/存在的)列表头部

      * RPUSH/RPUSHX: 将值插入到(/存在的)列表尾部

      * LPOP : 移除并获取列表的第一个元素

      * RPOP: 移除并获取列表的最后一个元素

      * LTRIM: 保留指定区间内的元素

      * LLEN: 获取列表长度

      * LSET: 通过索引设置列表元素的值

      * LINDEX: 通过索引获取列表中的元素

      * LRANGE: 获取列表指定范围内的元素

  • 相关阅读:
    android绘画折线图二
    android绘画折线图一
    jQuery Select操作大集合
    jquery事件切换hover/toggle
    jquery $(document).ready() 与window.onload的区别
    canny边缘检测 C++手动实现
    高斯核原理和C++实现
    darknet-yolov3 burn_in learning_rate policy
    UserWarning: indexing with dtype torch.uint8 is now deprecated, please use a dtype torch.bool instead
    BatchNormaliation
  • 原文地址:https://www.cnblogs.com/8013-cmf/p/12614212.html
Copyright © 2011-2022 走看看