高并发下,php、swoole与redis实现的秒杀功能

2020年08月13日 130 次阅读 0 条评论 1 人点赞

抢购、秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个:高并发对数据库产生的压力、竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到用缓存来处理抢购,避免直接操作数据库,例如使用Redis。结合swoole后,性能超群~~

秒杀常见就是秒杀超卖问题,就是有一个商品抢购活动,一个商品假如有100件库存,但是在抢购时有200人来抢购,这时就会并发,原本只有100的库存但是抢购的人过多,就会发生数据库里原本只有100的库存但是库存为0的时候还会有人提交成功,这就是超卖。

直接上案例~~

3张测试表

CREATE TABLE `mall_test_log` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `event` varchar(255) NOT NULL,
  `type` tinyint(4) NOT NULL DEFAULT '0',
  `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='日志表';

CREATE TABLE `mall_test_order` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `order_sn` varchar(32) NOT NULL,   `user_id` int(11) NOT NULL,   `goods_id` int(11) NOT NULL DEFAULT '0',   `sku_id` int(11) NOT NULL DEFAULT '0',   `price` float NOT NULL,   `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,   PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表'; CREATE TABLE `mall_test_store` (   `id` int(11) NOT NULL AUTO_INCREMENT,   `goods_id` int(11) NOT NULL,   `sku_id` int(10) unsigned NOT NULL DEFAULT '0',   `number` int(10) NOT NULL DEFAULT '0',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='库存表';

INSERT INTO `mall_test_store` (`id`, `goods_id`, `sku_id`, `number`) VALUES ('1', '1', '11', '1000');


代码:

/**

* 改良版 利用swoole的Channel通道

* 协程下 秒杀案例(5000次请求 并发1000) ab -n 5000 -c 1000 http://192.168.99.100:9555/order/skill * @throws \Psr\Container\ContainerExceptionInterface * @throws \Psr\Container\NotFoundExceptionInterface */ public function skill() { $container = ApplicationContext::getContainer(); $redis = $container->get(Redis::class); co(function () use ($redis) { $chan = new Channel(1); co(function () use ($chan, $redis) { $key = 'sku_id'; $count = $redis->lPop($key); $chan->push($count); }); co(function () use ($chan, $redis) { $chanData = $chan->pop(); if (!$chanData) { $this->log('库存为0,已售罄', 0); } else { $sku_id = 11; // 插入订单 $order_sn = getIdGenerator(); $data = [ 'order_sn' => $order_sn, 'user_id' => 1, 'goods_id' => 1, 'sku_id' => 11, 'price' => 10 ]; $sql2 = "update mall_test_store set number=number-1 where sku_id='{$sku_id}'"; if (Db::table('test_order')->insert($data) && DB::update($sql2)) { $this->log('下单成功', 1); } else { $this->log('下单失败', -1); } } }); }); } // 设置redis private function setkey($key, $count) { $container = ApplicationContext::getContainer(); $redis = $container->get(Redis::class); for ($i = 0; $i < $count; $i++) { $redis->lPush($key, 1); } } //记录日志 private function log($event, $type = 0) { $sql = "insert into mall_test_log(event,type)values('{$event}','{$type}')"; Db::insert($sql); var_dump($event); } // 初始化所有变量 public function clean() { $sku_id = 11; Db::delete("truncate table mall_test_log"); Db::delete("truncate table mall_test_order"); Db::update("update mall_test_store set number=1000 where sku_id='{$sku_id}'"); // 在redis中设置一下sku的数量。 $this->setkey('sku_id', 1000); }

讲梦想、讲奋斗可以,前提是钱要给够。

文章评论(0

接收回复邮件通知
非注册会员初次评论需要审核,审核时间(09:00-18:00),请耐心等待...