• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • php + redis 实现商城高并发秒杀

    秒杀系统,库存只有一份,所有人会在集中的时间读和写这些数据,多个人读一个数据。例如:小米手机每周二的秒杀,可能手机只有1万部,但瞬时进入的流量可能是几百几千万,瞬时流量非常多,都读相同的库存。读写冲突,MySQL数据库锁非常严重。并发高响应慢,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小。

    优化方向重点:

    • 查询页面使用redis 缓存。以12306为例,这是一个典型的读多些少的应用场景,大部分请求是商品查询,下单和支付才是最终的请求。手机只有1万部,100万个人来买,最多1万个人下单成功,其他人都是查询库存,数据库写比例只有1%,读比例占99%。
    • 秒杀抢购使用redis列表数据类型(List)。因为pop操作是原子的,即使有很多用户同时到达,也是依次执行。将请求尽量拦截在MySQL数据库操作上游。只有购买成功的再写入数据库中,减少了数据库频繁的操作。以购买火车票为例,一趟火车其实只有2000张票,200w个人来买,最后购买成功的只有2千人的数据。

    秒杀商品其实就是一个将集合中的商品id取出和用户id绑定的过程。只是这个过程进行的非常的快。那么我们将秒杀分为两步,如果秒杀成功,则记录下用户id和商品id 也就是所谓的秒杀订单。如果秒杀失败,我们则简单的记录一个秒杀失败的人数。来确定这次秒杀有多少有效用户参与。

    示例

    我们先假设我们有10000个人来抢10件商品。那么我们就在我们的商品库里面装载10件的商品id。

    <?php
     
    class Seckill
    {
    	const $listKey='goods_list';
    	const $buyKey='buy_list';
    	static protected $goodsNum;
    	static protected $userId;
    	static protected $redis;
    	static protected $redisConnect;
    
    	public function __construct()
    	{
    		if(empty(self::$redisConnect))
    		{
    			self::redis = new Redis();
    			self::redisConnect = self::redis->connect('127.0.0.1', 6379);
    		}
    		
    	}
    
    
    	static public function addGoods()
    	{
    		if(empty(self::goodsNum))
    		{
    			self::goodsNum=10;
    			for ($i=1;$i<=self::goodsNum;$i++) { $redis->rPush(self::listKey,$i);
    			}
    		}
    	}
    
    	static public function kill($userId=0)
    	{
    		if(!empty($userId))
    		{
    			self::userId=$userId;
    			if($goodsId=self::redis->lPop(self::listKey))
    			{
    			    self::redis->hSet(self::buyKey,$goodsId,self::userId);
    			    self::goodsNum--;
    			}
    			else
    			{
    			    self::goodsNum=0;
    			    //最后再把全部用户ID存入mysql数据库
    			}
    		}
    	}
    
    }
    

    第一步:装载商品

    require(Seckill.php);
    Seckill:: addGoods;
    

    第一步:开始秒杀

    require(Seckill.php);
    Seckill:: kill($userId);