秒杀场景一般需要需要解决的问题就是超卖和数据库压力。

一般解决数据库商品超卖最直接的方法就是将字段设置为unsigned(无符号),这样库存就不会为负了。

还有就是可以使用事务锁住操作的行,保证库存。当然也使用锁机制(悲观锁、乐观锁),但是这几种都是直接操作数据库的,并且用锁的话在其他场景(比如不参与抢购原价购买的用户),在高并发场景下可能会造成阻塞,直接影响数据库的性能。

还有种方案就是使用文件排他锁的机制,这里不进行介绍了,有兴趣的可以网上看一下。

redis的list数据结构底层实现就是双端链表,链表中的每个节点都保存了一个整数值。redis链表经常会被用于消息队列的服务,以完成多程序之间的消息交换。链表支持前后插入以及前后取出,所以如果往尾部插入元素,往头部取出元素,实现先进先出,这就是一种消息队列。下来就开始介绍redis中使用list链表实现秒杀的简单消息队列。

以下用到的redis语法简单介绍

LPUSH:将值插入到列表的头部

LPOP:移除并返回列表key的头元素

LLEN:返回列表key的长度

LRANGE:返回列表key中指定区间内的元素

EXPIRE:为key设置生存时间

EXPIREAT:为key设置到期时间

第一步:将参加抢购的商品加入队列<?php

$redis=newRedis();
$redis->connect('127.0.0.1','6379');
// 参加抢购的商品库存
$num=100;
// 将商品写入reids链表
for($i=0;$i
$redis->lpush('goods',1);
}
// 返回链表长度
var_dump($redis->llen('goods'));
// 查看链表中的全部元素
var_dump($redis->lrange('goods',0,-1));
我们可以使用定时任务为redis设置结束时间,或者在抢购方法判断结束时间
// 设置抢购时间为60s
$redis->expire('goods','60');
//设置抢购过期时间为12点 注意expireat方法只能接受时间戳参数
$redis->expireat('goods',strtotime('2019-03-01 12:00:00'));

第二部:下单执行出队列操作<?php

$redis=newRedis();
$redis->connect('127.0.0.1','6379');
//移除链表的头元素 减一次库存
$res=$redis->lPop('goods_store');
if($res){
echo"秒杀成功";
// 执行入库操作
}else{
echo"秒杀结束";
}

使用ab工具模拟高并发下的测试

ab-n1000-c1000http://192.168.73.129/pop.php

高性能系统的优化原则: 写入内存而不是写入硬盘、异步处理而不是同步处理、分布式处理。