问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

PHP + Redis 实现秒杀功能详解

创作时间:
作者:
@小白创作中心

PHP + Redis 实现秒杀功能详解

引用
1
来源
1.
https://www.cnblogs.com/mklblog/p/18121829

本文通过一个简单的示例演示了如何使用PHP和Redis实现秒杀功能。文章详细介绍了使用ThinkPHP6框架、Redis6.0、PHP7.4以及Predis操作包来实现秒杀功能,并提供了具体的代码实现和测试步骤。

一、概述

秒杀这个问题在很多面试的时候都会提到,会有各个方面的调优,配置等等,本文在这里举个简单的例子来演示下秒杀的过程,供大家参考发散思维。

二、准备介绍

  • thinkPHP6框架
  • redis6.0
  • php7.4
  • redis操作包predis
  • apache-jmeter-5.6

三、代码实现

秒杀controller层的简易代码:

<?php
/**
 * Created by PhpStorm
 * Author: fengzi
 * Date: 2024/4/8
 * Time: 10:07
 */
namespace app\admin\controller\seckill;
use app\admin\service\seckill\SeckillService;
class SeckillController
{
 public function __construct()
 {
 }
 /**
 * 设置商品数量
 * @param SeckillService $seckillService
 * @return \think\response\Json
 * @Author: fengzi
 * @Date: 2024/4/8 10:00
 */
 public function setNumber(SeckillService $seckillService)
 {
 $seckillService->setProductNumber();
 try {
 $seckillService->setProductNumber();
 return success('设置成功');
 } catch (\Exception $e) {
 return error($e->getMessage());
 }
 }
 /**
 * 购买入口
 * @param SeckillService $seckillService
 * @return \think\response\Json
 * @Author: fengzi
 * @Date: 2024/4/8 11:00
 */
 public function index(SeckillService $seckillService)
 {
 try {
 $seckillService->seckill();
 return success();
 } catch (\Exception $e) {
 return error($e->getMessage());
 }
 }
}

秒杀service层的简易代码:

<?php
/**
 * Created by PhpStorm
 * Author: fengzi
 * Date: 2024/4/8
 * Time: 10:09
 */
namespace app\admin\service\seckill;
use Predis\Client;
class SeckillService
{
 private Client $redisService;
 public function __construct()
 {
 // 初始化Redis连接
 $this->redisService = new Client(config('cache.stores.redis'));
 }
 /**
 * 设置秒杀的产品数量
 * @return void
 * @Author: fengzi
 * @Date: 2024/4/8 10:11
 */
 public function setProductNumber()
 {
 $this->redisService->set('product_number', 20);
 }
 /**
 * 实现秒杀过程
 * 一个用户只能购买一次商品
 * @return void
 * @Author: fengzi
 * @Date: 2024/4/8 16:45
 */
 public function seckill()
 {
 //lua脚本,保证原子性
 $lua_script = <<<LUA
local number = redis.call('get', KEYS[1])
if (redis.call('exists', KEYS[1]) == 1 and tonumber(number) > 0 ) then
 redis.call('decr', KEYS[1])
 redis.call('sadd', KEYS[2], ARGV[1])
 return true
else
 return false
end
LUA;
 //模拟用户ID
 $userId = mt_rand(1, 50);
 //抢购成功的用户列表
 $userLists = 'user_lists_id';
 try {
 $res = $this->redisService->sismember($userLists, $userId);
 if ( $res == 0 ) {
 $result = $this->redisService->eval($lua_script, 2, 'product_number', 'user_lists_id', $userId);
 if ($result) {
 // 处理订单逻辑
 echo "秒杀成功!!!!";
 } else {
 echo "秒杀结束!";
 }
 } else {
 echo "秒杀失败,用户{$userId}已经秒杀过了!";
 }
 } catch (\Exception $e) {
 echo "秒杀失败:" . $e->getMessage();
 }
 }
}

接口路由:

<?php
use think\facade\Route;
/**秒杀*/
Route::group('/seckill', function () {
 //设置秒杀数量
 Route::any('set_number', '/seckill.Seckill/setNumber');
 //购买
 Route::any('index', '/seckill.Seckill/index');
});

四、测试

使用jmeter对接口进行压测,查看是否有超卖,一人购买多次的情况。

1、设置20个商品数量

2、配置 jmeter ,一秒请求200个,请求结果如下:

3、如下图所示,20个商品已经售卖完,未出现超卖。

4、如下图所示,购买人是20个,且无重复购买的人。

五、总结

实际生产环境的“秒杀”业务是很繁杂的,有许多地方需要注意。本文只是对“秒杀”这个场景做了简单的试验,仅给大家提供个思路和样本。如有不同见解欢迎大家留言讨论。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号