【面试题22】PHP通过Redis和MySQL实现商品秒杀功能

news2025/1/12 22:47:54

文章目录

  • 一、前言
  • 二、系统架构
  • 三、技术栈
  • 四、系统设计
    • 4.1 商品设计
    • 4.2 用户设计
    • 4.3 抢单设计
    • 4.5 并发控制
    • 4.5 获取用户购买记录代码
    • 4.7 扣减商品库存代码
    • 4.8 获取商品锁代码
    • 4.9 添加订单记录代码
  • 总结


一、前言

本文已收录于PHP全栈系列专栏:PHP面试专区。
计划将全覆盖PHP开发领域所有的面试题,对标资深工程师/架构师序列,欢迎大家提前关注锁定。

随着互联网的快速发展,电商行业也在不断地蓬勃发展。其中,电商秒杀活动已经成为了电商行业重要的促销方式之一。在电商秒杀活动中,用户可以以非常低的价格抢购到商品,从而带来了极高的转化率和客户满意度。但是,由于秒杀活动的特殊性,系统对性能和安全等方面的要求都很高。

本文将介绍一个基于PHP、Redis、MySQL的电商秒杀系统的实现方案,并给出核心关键代码。
在这里插入图片描述

二、系统架构

整个秒杀系统分为四层,分别是Web层、应用层、数据层和存储层。其中:

  • Web层:负责接收用户的请求,处理请求参数和验证请求的合法性。
  • 应用层:负责对用户请求进行逻辑处理,包括判断该用户是否有抢购资格、判断商品库存是否充足等。
  • 数据层:负责操作数据库,通过读写分离提升数据库性能。
  • 存储层:负责缓存商品信息和用户信息,通过Redis集群提升系统性能。

三、技术栈

本系统使用的技术栈如下:

  • Nginx:Web服务器,负责接收用户请求并分发到应用层。
  • PHP:服务端编程语言,用于实现应用层。本系统使用最新的PHP 8版本。
  • Redis:内存缓存数据库,用于缓存商品和用户信息。
  • MySQL:关系型数据库,用于存储商品和订单信息。
  • Docker:容器化部署工具,用于统一环境和简化部署。

四、系统设计

4.1 商品设计

在秒杀系统中,商品是核心资源之一。本系统中,商品数据表结构如下:

CREATE TABLE `goods` (
    `id` INT UNSIGNED AUTO_INCREMENT COMMENT '商品ID',
    `name` VARCHAR(128) NOT NULL COMMENT '商品名称',
    `price` DECIMAL(10,2) UNSIGNED NOT NULL DEFAULT '0.00' COMMENT '商品单价',
    `total` INT UNSIGNED NOT NULL COMMENT '商品库存',
    `sold` INT UNSIGNED NOT NULL DEFAULT '0' COMMENT '已售数量',
    `start_time` TIMESTAMP NOT NULL COMMENT '秒杀开始时间',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
  • id:商品ID,自增主键。
  • name:商品名称。
  • price:商品单价,精确到两位小数。
  • total:商品总库存。
  • sold:已经售出的数量。
  • start_time:秒杀开始时间,建议为具体时间点,如2023-07-01 12:00:00

4.2 用户设计

在秒杀系统中,用户需要进行身份验证和抢购资格判断。本系统中,用户数据表结构如下:

CREATE TABLE `user` (
    `id` INT UNSIGNED AUTO_INCREMENT COMMENT '用户ID',
    `name` VARCHAR(32) NOT NULL COMMENT '用户名',
    `mobile` VARCHAR(11) NOT NULL COMMENT '手机号',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
  • id:用户ID,自增主键。
  • name:用户名。
  • mobile:用户手机号,用于身份验证和资格判断。

需要注意的是,为了提高系统性能,建议使用Redis缓存用户信息,避免频繁地查询数据库。

4.3 抢单设计

在秒杀系统中,抢单是最核心的功能之一。抢单的处理流程如下:

  1. 用户发起请求,包含用户ID和商品ID。
  2. 系统首先从Redis缓存中获取该用户的购买记录,如果该用户已经购买过该商品,则直接返回“重复购买”错误。
  3. 如果该用户没有购买记录,则从Redis中获取该商品的库存数量。如果库存已经为0,则直接返回“商品已经抢光”错误。
  4. 如果该商品还有库存,则从Redis的队列中取出一个商品锁,如果队列已经空了,则直接返回“当前人数过多,请稍后再试”错误。
  5. 如果取到了商品锁,则进行事务操作,将商品库存减1,并在MySQL中添加订单记录。如果事务提交成功,则返回“抢购成功”,否则返回“系统繁忙,请稍后再试”错误。

商品锁是本系统的核心设计之一。为了保证系统的性能和可靠性,本系统采用Redis的队列实现商品锁。可以通过如下代码很容易地实现队列的获取和释放:

// 获取锁
$lock = $redis->blPop('goods_lock:' . $goodsId, 10);
if (!isset($lock[1])) {
    throw new RuntimeException('当前人数过多,请稍后再试');
}
// 处理业务逻辑
// ...
// 释放锁
$redis->rPush('goods_lock:' . $goodsId, 1);

4.5 并发控制

在秒杀活动中,高并发是不可避免的问题。因此,在系统设计中需要考虑如何处理高并发请求。本系统采用如下措施来处理高并发:

  1. 使用Redis缓存,减轻数据库的压力。
  2. 前端限流,避免过多的请求直接打到系统中。例如,可以设置每个用户每秒只能发起一次请求。
  3. 后端限流,避免请求过多导致系统崩溃。例如,可以使用semaphoreredis进行请求频率控制。
  4. 使用Redis的队列实现商品锁,避免并发造成的销量超额问题。

4.5 获取用户购买记录代码

// 缓存用户的购买记录
$key = 'user_buy:' . $userId . '_' . $goodsId;
$count = $redis->get($key);
if (false !== $count) {
    if ($count >= 1) {
        throw new RuntimeException('重复购买');
    }
}

4.7 扣减商品库存代码

// 检查库存是否足够
$stockKey = 'goods_stock:' . $goodsId;
$total = $redis->get($stockKey);
if ($total <= 0) {
    throw new RuntimeException('商品已经抢光');
}
// 将库存数减1
$newStock = $redis->decr($stockKey);
if ($newStock < 0) {
    // 库存不足
    $redis->incr($stockKey);
    throw new RuntimeException('商品已经抢光');
}

4.8 获取商品锁代码

// 获取锁
$lock = $redis->blPop('goods_lock:' . $goodsId, 10);
if (!isset($lock[1])) {
    throw new RuntimeException('当前人数过多,请稍后再试');
}
// 处理业务逻辑
// ...
// 释放锁
$redis->rPush('goods_lock:' . $goodsId, 1);

4.9 添加订单记录代码

// 添加订单记录
$data = [
    'order_no' => $orderNo,
    'user_id' => $userId,
    'goods_id' => $goodsId,
    'goods_name' => $goods['name'],
    'goods_price' => $goods['price'],
    'ctime' => time(),
];
$res = $this->db->table('order')->insert($data);
if (empty($res)) {
    throw new RuntimeException('添加订单失败');
}

总结

本文介绍了一个基于PHP、Redis、MySQL的电商秒杀系统的实现方案,并给出了核心代码。

本文已收录于PHP全栈系列专栏:PHP面试专区。
计划将全覆盖PHP开发领域所有的面试题,对标资深工程师/架构师序列,欢迎大家提前关注锁定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/693856.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

element ui - el-table 设置表头背景颜色和字体颜色

element ui - el-table 设置表头背景颜色和字体颜色 场景代码效果 场景 在使用 elementui 中的 el-table 时&#xff0c;由于默认表格样式与设计稿不符&#xff0c;需要将表头的背景色和字体颜色设置为新颜色。 但是对 thead&#xff0c;thead tr&#xff0c;.el-table__cell…

go mod tidy 提示错误 go mod tidy -go=1.16 go mod tidy -go=1.17

错误概览 执行 go mod tidy 时&#xff0c;提示如下错误 > go mod tidy github.com/myrepo/myproj importsgo.k6.io/k6 importsgo.k6.io/k6/cmd importsgithub.com/fatih/color loaded from github.com/fatih/colorv1.12.0,but go 1.16 would select v1.13.0To upgrade to t…

通付盾发布WAAP白皮书,帮助企业应对数字化转型过程中日益高发的网络安全威胁

简介 企业数字化转型是数字经济发展的重要一环。面对企业数字化转型过程中的安全问题&#xff0c;WAAP白皮书将对攻击方式、攻击量、攻击来源、行业分布等维度对各类攻击进行详细解读&#xff0c;梳理传统Web应用防护能力的不足&#xff0c;分析日益增长的API防护&#xff0c;…

新手小白学JAVA_IDEA修改关键字和注释颜色

很多小白在刚刚使用IDEA的时候还不是很熟练 本文主要给大家提供一些使用的小技巧&#xff0c;希望能帮助到你 1.修改关键字的默认颜色 对于很多初学者而言&#xff0c;将Java的关键字设置成一个特殊的颜色有助于记忆 我们一起来试一试吧~ 2.修改注释的默认颜色 注释颜色默认都…

23JS14——Web APIs简介

文章目录 一、Web APIs和JS基础关联性1、JS组成2、基础阶段以及Web APIs阶段1、API2、Web APIs 二、API和Web APIs 目标&#xff1a; 1、Web APIs和JS基础关联性 2、API和Web APIs 一、Web APIs和JS基础关联性 1、JS组成 2、基础阶段以及Web APIs阶段 1、API 2、Web APIs 二…

elasticsearch7.17.3实现按terms传入内容排序,类似mysql中order by filed()的排序方式

现有一个需求&#xff0c;需要在elasticsearch中实现用terms筛选内容&#xff0c;并且按terms传入的内容顺序排列 类型于mysql中order by filed()的排序方式&#xff0c;具体实现如下 目录 一、需求二、整体思路三、es查询语句四、java生成es连接五、java调用es六、最终实现结果…

【数据结构】线性表——顺序表 链式表 单链表 双链表 循环链表 静态链表

文章目录 二 线性表1.定义与基本操作2.顺序表2.1 定义2.2 静态分配 3.链式表3.1 单链表3.1.1 定义3.1.2 建立1&#xff09;头插法建立单链表2&#xff09;尾插法建立单链表 3.1.3 插入3.1.4 删除3.1.5 查找1&#xff09;按序号查找结点2&#xff09;按值查找结点 * 单链表完整代…

【 云原生 kubernetes 】- 使用Filebeat采集k8s日志

文章目录 简介采集流程&#xff1a;镜像构建 Helm部署配置调整删除赘余部分更新Values挂载文件 主流程结果 ⚡️: 日志采集器Logstash其功能虽然强大&#xff0c;但是它依赖java、在数据量大的时候&#xff0c;Logstash进程会消耗过多的系统资源&#xff0c;这将严重影响业务系…

制作外网shell

安装配置环境 网址 https://www.ngrok.cc 支付两块钱之后&#xff0c;就可以免费使用服务器&#xff0c;进行隧道开通。远程端口就是服务器的端口&#xff0c;可以先随便写&#xff0c;等下会提示有哪一些可用端口 下载客户端&#xff0c;选择64位的版本&#xff08;跟kali一…

Lock接口介绍

1、 Synchronized (1) 多线程编程模板上 1) 第一步&#xff1a;创建资源类。 2) 第二步&#xff1a;创建操作方法。 3) 第三步&#xff1a;创建线程调用操作方法。 4&#xff09;原则&#xff1a;高内聚低耦合。 (2) 实现步骤 1&#xff09;创建资源类。 2&#xff09;资源类…

ubuntu下简单编译opencv

# 安装依赖&#xff0c;其中python-dev没有安装上没关系。 sudo apt install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libpng-dev libjpeg-dev libtiff5-dev libswscale-dev libjasper-dev libdc1394-22-dev libtiff-dev python-dev python-numpy libtb…

chatgpt赋能python:Python轨迹可视化:用数据讲故事

Python轨迹可视化&#xff1a;用数据讲故事 介绍 随着物联网、智能城市等领域的发展&#xff0c;越来越多的数据被收集下来并存储在数据库中。这些数据对于决策者来说是非常重要的&#xff0c;但是如何将这些数据进行展示和分析呢&#xff1f;这时候Python轨迹可视化就可以派…

java项目之后勤管理系统ssm源码+文档

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的后勤管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

【docker创建容器的坑】WSL启动nvidia-docker镜像:报错libnvidia-ml.so.1- file exists- unknown

可能得错误一&#xff1a; 今天使用docker创建容器的时候总是出错&#xff0c;最后锁定问题在“--gpus all”这里&#xff1a; 不加--gpu all可以运行&#xff0c;加入了--gpus all就出错&#xff1a; docker: Error response from daemon: failed to create task for conta…

C语言进阶(反码和补码)

文章目录 前言一、反码二、补码三、符号位四、在程序中打印反码和补码总结 前言 本篇文章我们主要来讲解C语言中的反码和补码&#xff0c;这个可能是大家比较容易忽略的点&#xff0c;那么这篇文章就带大家来学习一下什么是反码和补码。 一、反码 反码是通过对整数的二进制表…

基于FPGA的多通道数据采集单元同步的实现

多通道数据采集设备在当前信息数字化的时代应用广泛&#xff0c;各种被测量的信息 如光线、温度、压力、湿度、位置等&#xff0c;都需要经过多通道信号采集系统的采样和 处理&#xff0c;才能被我们进一步分析利用[37]。在一些对采集速率要求较高的军事、航天、 航空、工业制造…

【JUC进阶】06. 轻量级锁

目录 1、前言 2、轻量级锁 2.1、什么是轻量级锁 2.2、工作原理 2.2.1、回顾Mark Word 2.2.2、工作流程 2.3、解锁 3、适用场景 4、注意事项 5、轻量级锁与偏向锁的对比 6、小结 1、前言 前面一节我们讲到了偏向锁。当偏向锁被撤销&#xff0c;或其他线程竞争的时候…

国内脑机接口最新研究 - 脑控机器人在非结构化场景执行避障任务

&#x1f482; 个人主页: 同学来啦&#x1f91f; 版权: 本文由【同学来啦】原创、在CSDN首发、需要转载请联系博主 &#x1f4ac; 如果文章对你有帮助&#xff0c;欢迎关注、点赞、收藏和订阅专栏哦 文章目录 &#x1f31f; 一、能避障的脑控机器人导航&#x1f31f; 二、机器…

Moveit1 机器人配置包超详细解析

前言 Moveit的robot_moveit_config包里有很多文件&#xff0c;在配置时容易搞不清关系&#xff0c;导致配置过程艰难、漫长。 同时互联网上的文档几乎没有详细介绍这部分配置包的&#xff0c;大神各有各的配法&#xff0c;比较混乱。 因此笔者整理了配置包内各个重要的文件&…

Volvo EDI 项目 MySQL 方案开源介绍

近期为了帮助广大用户更好地使用 EDI 系统&#xff0c;我们根据以往的项目实施经验&#xff0c;将成熟的 EDI 项目进行开源。用户安装好知行之桥 EDI 系统之后&#xff0c;只需要下载我们整理好的示例代码&#xff0c;并放置在知行之桥指定的工作区中&#xff0c;即可开始使用。…