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 '恭喜您抢购成功!';

}

相关推荐
光影少年17 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
As977_18 分钟前
前端学习Day12 CSS盒子的定位(相对定位篇“附练习”)
前端·css·学习
susu108301891120 分钟前
vue3 css的样式如果background没有,如何覆盖有background的样式
前端·css
Ocean☾22 分钟前
前端基础-html-注册界面
前端·算法·html
Dragon Wu24 分钟前
前端 Canvas 绘画 总结
前端
CodeToGym28 分钟前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化
~甲壳虫29 分钟前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js
~甲壳虫33 分钟前
说说webpack proxy工作原理?为什么能解决跨域
前端·webpack·node.js
Cwhat35 分钟前
前端性能优化2
前端
SameX37 分钟前
鸿蒙 Next 电商应用安全支付与密码保护实践
前端·harmonyos