redis秒杀(PHP版本)

前提提要

今天产品端提了个需求,院校组要求借调我去帮忙,因为我以前做过商城,现在他们需求做一个积分商城,需要做一个秒杀模块,结果毫无意外的我被借调过去了,刚好可以复习一下以前的知识,现在介绍一下redis的秒杀机制,首先大家需要了解redis的基础信息,可以参考小编的前面的文章 https://blog.csdn.net/masterphp/article/details/130728703,现在开始介绍redis秒杀,只要有两种方式来实现,锁和队列

秒杀流程概述

在进行Redis的实现之前,我们先简要介绍一下秒杀的流程:

1.用户在前端页面选择要秒杀的商品,并提交订单;

2.系统校验用户提交的订单是否合法,如商品库存是否充足、用户是否符合参加条件等;

3.系统将订单信息写入数据库,并返回给用户订单处理中的状态;

4.用户在订单处理中不断轮询订单状态,直到订单完成。

在秒杀流程中,系统需要进行多次校验和处理,其中最关键的部分就是判断商品库存是否充足,这部分需要保证数据的可靠性和并发性。

秒杀实现原理

1.使用Redis队列存储订单:秒杀活动中,会有大量的订单请求同时涌入系统,若采用传统的关系型数据库进行存储,会导致并发量过大,数据库连接数量过多,从而降低系统的性能和稳定性。为了解决这个问题,我们可以使用队列来存储订单信息。Redis中提供了List类型的数据结构,可用作队列的实现。通过将订单信息放入Redis队列中,系统可以快速处理大量的订单请求,并以先到先服务的顺序进行处理。

2.使用Redis预减库存:秒杀活动中,需要对商品库存进行实时监控,否则会因为库存不够而导致订单处理失败。但是,如果每个订单都从数据库中查询商品库存,将会增加数据库的压力,导致系统并发性能降低。

为了解决这个问题,我们可以使用Redis的预减库存策略。当用户抢购时,我们先使用Redis缓存中的商品库存信息,并实时更新该缓存信息,如果库存已经减少至0,则直接返回秒杀失败。

3.使用Redis分布式锁:在秒杀活动中,虽然我们使用Redis队列和预减库存,但是系统仍然需要面对大量的并发请求。在这些请求中,可能会有多个用户同时对同一个商品进行抢购,这就需要我们使用分布式锁来保证数据的可靠性。Redis的分布式锁使用方式非常简单,在抢购开始时,我们使用Redis的SETNX操作来尝试获取分布式锁,如果获取成功,则对库存进行操作,操作完成后,释放分布式锁。如果获取分布式锁失败,则需等待一段时间重试。

乐观锁实现方式

//添加商品库存

public function addGoodsStock(Request $request)

{

//接受数据

goods_id = request->input('goods_id');

store = request->input('store');

//设置商品库存的key

key = 'seckill_goods_id_'.goods_id;

res = Redis::set(key,$store);

return $res;

}

//用户秒杀商品

public function buy(Request $request)

{

//接受数据

uid = request->input('uid');

goods_id = request->input('goods_id');

//商品库存key

key = 'seckill_goods_id_'.goods_id;

//监听对应的key,事务提交之前,如果key被修改,则事务被打断

Redis::watch($key);

//获取商品库存

store = Redis::get(key);

//抢购成功用户集合key

$setKey = 'userGoodsSuccess';

//判断该用户是否已经抢购过(该用户id是否在抢购用户集合中)

userBuyStatus = Redis::sismember(setKey,$uid);

if($userBuyStatus){

return '您已抢过!';

}

if($store){

//记录用户信息,更新库存(保证这一组命令,要么全部成功,要么都不成功)

Redis::multi();//开始事务

Redis::decr($key);//减少库存

//将用户id添加到抢购成功用户集合中

Redis::sadd(setKey,uid);

$result = Redis::exec();//提交,判断当前的key是否被某个客户端修改了

//结果判断

if($result){

//操作数据库,修改商品库存销量

DB::table('goods')->where('id', $goods_id)->decrement('stock', 1, ['sale' => DB::raw('`sale`+1')]);

//创建订单信息

return '抢购成功!';

}else{

return '抢购失败,请重试!';

}

}else{

return '已抢光!';

}

}

队列实现方式

//初始化库存队列

public function init(Request $request)

{

//接受参数

goods_id = request->input('goods_id');

store = request->input('store');

//获取key

key = 'list_seckill_goods_id_'.goods_id;

//防止对已经设置过的商品库存进行覆盖

if(!empty(Redis::llen($key))) {

return '已经设置了库存';

}

//初始化缓存,删除抢购用户id队列key和成功信息保存key,这个是抢购时生成的,防止错乱,初始化删除

userListKey = 'user_goods_id_'.goods_id;

Redis::command('del', [$userListKey, 'success']);

// 将商品存入Redis链表中

for(i = 1; i <= store; i++) {

Redis::lpush(key, i);

}

//设置过期时间

Redis::expire($key, 120);

echo '商品存入队列成功,数量:'.Redis::llen($key);

}

//redis队列抢购

public function start(Request $request)

{

// 模拟随机登录用户

$uid = mt_rand(1, 9999);

//uid = request->input('uid');

goods_id = request->input('goods_id');

//获取key

key = 'list_seckill_goods_id_'.goods_id;

//从链表的头部删除一个元素,返回删除的元素,因为pop操作是原子性,即使很多用户同时到达,也是依次执行

count = Redis::lpop(key);

if (!$count) {

return '已抢光!';

}

//已抢购用户id队列

userListKey = 'user_goods_id_'.goods_id;

//判断该用户是否已经抢购过(该用户id是否在抢购用户集合中)

userBuyStatus = Redis::sismember(userListKey,$uid);

if($userBuyStatus){

return '您已抢过!';

}

//将用户id添加到抢购成功用户集合中

Redis::sadd(userListKey,uid);

msg = '抢到的人为:'.uid.',抢到商品的顺序为第'.$count.'个';

Redis::lpush('success', $msg);

//操作数据库,修改商品库存销量

DB::table('goods')->where('id', $goods_id)->decrement('stock', 1, ['sale' => DB::raw('`sale`+1')]);

//创建订单信息

return '恭喜您抢购成功!';

}

相关推荐
0思必得06 小时前
[Web自动化] Selenium处理动态网页
前端·爬虫·python·selenium·自动化
东东5166 小时前
智能社区管理系统的设计与实现ssm+vue
前端·javascript·vue.js·毕业设计·毕设
catino6 小时前
图片、文件的预览
前端·javascript
Leon-zy7 小时前
Redis7.4.5 主备冗余+哨兵模式部署
redis·哨兵模式·主备模式
layman05288 小时前
webpack5 css-loader:从基础到原理
前端·css·webpack
半桔8 小时前
【前端小站】CSS 样式美学:从基础语法到界面精筑的实战宝典
前端·css·html
AI老李8 小时前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·postcss
_OP_CHEN8 小时前
【前端开发之CSS】(一)初识 CSS:网页化妆术的终极指南,新手也能轻松拿捏页面美化!
前端·css·html·网页开发·样式表·界面美化
啊哈一半醒8 小时前
CSS 主流布局
前端·css·css布局·标准流 浮动 定位·flex grid 响应式布局
PHP武器库8 小时前
ULUI:不止于按钮和菜单,一个专注于“业务组件”的纯 CSS 框架
前端·css