发布日期:2023年12月
阅读时间:18分钟
一、项目背景与架构设计
在电商业务中,秒杀活动是考验系统性能的重要场景。本文基于ThinkPHP 8.0框架,设计一套能够支撑万级并发的高性能秒杀系统。我们将从数据库设计、缓存策略、队列处理等多个维度进行深入讲解。
系统架构图:
用户层 → 负载均衡 → 应用层(ThinkPHP) → 缓存层(Redis) → 队列层 → 数据库层(MySQL)
↓
监控层(Prometheus)
核心技术栈:
- 核心框架:ThinkPHP 8.0 + PHP 8.1
- 缓存系统:Redis 6.0 + 分布式锁
- 消息队列:Redis List + 自定义队列驱动
- 数据库:MySQL 8.0 + 读写分离
- 监控系统:Prometheus + Grafana
二、数据库设计与模型层实现
2.1 数据表结构设计
-- 秒杀活动表
CREATE TABLE `seckill_activity` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL COMMENT '活动名称',
`product_id` bigint(20) NOT NULL COMMENT '商品ID',
`total_stock` int(11) NOT NULL COMMENT '总库存',
`available_stock` int(11) NOT NULL COMMENT '可用库存',
`start_time` datetime NOT NULL COMMENT '开始时间',
`end_time` datetime NOT NULL COMMENT '结束时间',
`status` tinyint(1) DEFAULT '1' COMMENT '状态:1启用 0禁用',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_time_status` (`start_time`,`end_time`,`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 秒杀订单表
CREATE TABLE `seckill_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`order_sn` varchar(32) NOT NULL COMMENT '订单号',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`activity_id` bigint(20) NOT NULL COMMENT '活动ID',
`product_id` bigint(20) NOT NULL COMMENT '商品ID',
`quantity` int(11) NOT NULL DEFAULT '1' COMMENT '购买数量',
`order_status` tinyint(1) DEFAULT '0' COMMENT '订单状态',
`created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_sn` (`order_sn`),
KEY `idx_user_activity` (`user_id`,`activity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.2 ThinkPHP模型实现
<?php
// app/model/SeckillActivity.php
namespace appmodel;
use thinkModel;
class SeckillActivity extends Model
{
protected $table = 'seckill_activity';
// 自动时间戳
protected $autoWriteTimestamp = true;
protected $createTime = 'created_at';
protected $updateTime = 'updated_at';
// 状态常量
const STATUS_ENABLED = 1;
const STATUS_DISABLED = 0;
/**
* 获取进行中的活动
*/
public static function getActiveActivities()
{
$now = date('Y-m-d H:i:s');
return self::where('status', self::STATUS_ENABLED)
->where('start_time', 'where('end_time', '>=', $now)
->select();
}
/**
* 减少库存
*/
public function decreaseStock($quantity = 1)
{
return $this->where('id', $this->id)
->where('available_stock', '>=', $quantity)
->dec('available_stock', $quantity)
->update();
}
}
// app/model/SeckillOrder.php
class SeckillOrder extends Model
{
protected $table = 'seckill_order';
protected $autoWriteTimestamp = true;
protected $createTime = 'created_at';
protected $updateTime = false;
// 订单状态
const STATUS_PENDING = 0;
const STATUS_SUCCESS = 1;
const STATUS_FAILED = 2;
/**
* 生成唯一订单号
*/
public static function generateOrderSn()
{
return date('YmdHis') . str_pad(mt_rand(1, 9999), 4, '0', STR_PAD_LEFT);
}
}
?>
三、Redis缓存与分布式锁设计
3.1 库存缓存策略
<?php
// app/service/SeckillService.php
namespace appservice;
use thinkfacadeCache;
use thinkfacadeLog;
class SeckillService
{
const STOCK_PREFIX = 'seckill:stock:';
const USER_LIMIT_PREFIX = 'seckill:user_limit:';
/**
* 初始化活动库存到Redis
*/
public static function initActivityStock($activityId, $stock)
{
$key = self::STOCK_PREFIX . $activityId;
return Cache::set($key, $stock, 3600); // 1小时过期
}
/**
* 预减库存
*/
public static function preDecreaseStock($activityId)
{
$key = self::STOCK_PREFIX . $activityId;
// 使用Lua脚本保证原子性
$lua = <<<LUA
local stock = redis.call('get', KEYS[1])
if not stock then
return -1 -- 缓存不存在
end
if tonumber(stock) eval($lua, [$key], 1);
switch ($result) {
case 1:
return true;
case 0:
throw new Exception('库存不足');
case -1:
throw new Exception('活动不存在或已结束');
default:
throw new Exception('系统异常');
}
}
/**
* 用户访问频率限制
*/
public static function checkUserLimit($userId, $activityId, $limit = 1)
{
$key = self::USER_LIMIT_PREFIX . $activityId . ':' . $userId;
$count = Cache::inc($key);
if ($count == 1) {
Cache::expire($key, 60); // 1分钟过期
}
if ($count > $limit) {
throw new Exception('操作过于频繁,请稍后重试');
}
return true;
}
}
?>
3.2 分布式锁实现
<?php
// app/service/DistributedLock.php
class DistributedLock
{
private $redis;
private $lockKey;
private $lockValue;
private $expireTime;
public function __construct($lockKey, $expireTime = 10)
{
$this->redis = Cache::store('redis')->handler();
$this->lockKey = 'lock:' . $lockKey;
$this->lockValue = uniqid();
$this->expireTime = $expireTime;
}
/**
* 获取锁
*/
public function acquire()
{
$result = $this->redis->set(
$this->lockKey,
$this->lockValue,
['NX', 'EX' => $this->expireTime]
);
return $result;
}
/**
* 释放锁
*/
public function release()
{
$lua = <<<LUA
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
LUA;
return $this->redis->eval($lua, [$this->lockKey, $this->lockValue], 1);
}
/**
* 自动释放锁的闭包执行
*/
public static function executeWithLock($lockKey, callable $callback, $expireTime = 10)
{
$lock = new self($lockKey, $expireTime);
try {
if (!$lock->acquire()) {
throw new Exception('系统繁忙,请稍后重试');
}
return $callback();
} finally {
$lock->release();
}
}
}
?>
四、秒杀核心业务流程
4.1 控制器层实现
<?php
// app/controller/Seckill.php
namespace appcontroller;
use appBaseController;
use appserviceSeckillService;
use appserviceDistributedLock;
use appmodelSeckillOrder;
use appmodelSeckillActivity;
use thinkfacadeQueue;
class Seckill extends BaseController
{
/**
* 秒杀入口
*/
public function seckill($activityId)
{
try {
$userId = $this->getUserId();
// 1. 基础校验
$this->validateParams($activityId, $userId);
// 2. 分布式锁防止重复提交
$lockKey = "seckill:{$activityId}:{$userId}";
$result = DistributedLock::executeWithLock($lockKey, function() use ($activityId, $userId) {
// 3. 用户频率限制
SeckillService::checkUserLimit($userId, $activityId);
// 4. Redis预减库存
SeckillService::preDecreaseStock($activityId);
// 5. 创建订单队列任务
$orderData = [
'user_id' => $userId,
'activity_id' => $activityId,
'create_time' => time()
];
Queue::push('SeckillOrderJob', $orderData);
return ['code' => 200, 'message' => '秒杀请求已接受,请等待结果'];
});
return json($result);
} catch (Exception $e) {
return json([
'code' => 500,
'message' => $e->getMessage()
]);
}
}
/**
* 参数验证
*/
private function validateParams($activityId, $userId)
{
if (!$userId) {
throw new Exception('请先登录');
}
$activity = SeckillActivity::find($activityId);
if (!$activity || $activity->status != SeckillActivity::STATUS_ENABLED) {
throw new Exception('活动不存在或已结束');
}
$now = time();
if ($now start_time)) {
throw new Exception('活动尚未开始');
}
if ($now > strtotime($activity->end_time)) {
throw new Exception('活动已结束');
}
}
/**
* 获取用户ID(根据实际业务实现)
*/
private function getUserId()
{
// 实际项目中从session或token中获取
return $this->request->uid ?? 0;
}
}
?>
4.2 队列任务处理
<?php
// app/job/SeckillOrderJob.php
namespace appjob;
use thinkqueueJob;
use appmodelSeckillOrder;
use appmodelSeckillActivity;
use thinkfacadeDb;
class SeckillOrderJob
{
public function fire(Job $job, $data)
{
try {
// 开启事务
Db::startTrans();
$userId = $data['user_id'];
$activityId = $data['activity_id'];
// 检查是否已经下单
$existingOrder = SeckillOrder::where([
'user_id' => $userId,
'activity_id' => $activityId
])->find();
if ($existingOrder) {
$job->delete();
return;
}
// 数据库层面扣减库存
$activity = SeckillActivity::find($activityId);
if (!$activity->decreaseStock()) {
throw new Exception('库存不足');
}
// 创建订单
$order = new SeckillOrder();
$order->save([
'order_sn' => SeckillOrder::generateOrderSn(),
'user_id' => $userId,
'activity_id' => $activityId,
'product_id' => $activity->product_id,
'order_status' => SeckillOrder::STATUS_SUCCESS
]);
Db::commit();
$job->delete();
// 发送成功通知
$this->sendSuccessNotification($userId, $order);
} catch (Exception $e) {
Db::rollback();
// 失败重试机制
if ($job->attempts() release(5); // 5秒后重试
} else {
$job->delete();
// 记录失败日志
thinkfacadeLog::error('秒杀订单创建失败:' . $e->getMessage());
}
}
}
private function sendSuccessNotification($userId, $order)
{
// 实际项目中集成消息推送
// 如短信、微信模板消息、APP推送等
}
}
?>
五、性能优化与监控
5.1 数据库连接池配置
// config/database.php
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => env('database.hostname', '127.0.0.1'),
'database' => env('database.database', ''),
'username' => env('database.username', ''),
'password' => env('database.password', ''),
'hostport' => env('database.hostport', '3306'),
'params' => [
PDO::ATTR_PERSISTENT => true, // 持久连接
PDO::ATTR_TIMEOUT => 5,
],
'charset' => 'utf8mb4',
'deploy' => 1, // 读写分离
'rw_separate' => true,
'master_num' => 1,
'slave_no' => '',
'fields_strict' => true,
'break_reconnect' => true,
'trigger_sql' => true,
'fields_cache' => false,
]
],
'breakpoint' => [
'enable' => true,
'time' => 60
]
];
5.2 Redis集群配置
// config/cache.php
return [
'default' => 'redis',
'stores' => [
'redis' => [
'type' => 'redis',
'host' => env('redis.host', '127.0.0.1'),
'port' => env('redis.port', 6379),
'password' => env('redis.password', ''),
'select' => 0,
'timeout' => 5,
'persistent' => true,
'serialize' => true,
'cluster' => [
'enable' => true,
'name' => null,
'seeds' => [
'redis1:6379',
'redis2:6379',
'redis3:6379'
],
],
],
],
];
5.3 性能监控中间件
<?php
// app/middleware/MonitorMiddleware.php
namespace appmiddleware;
use thinkResponse;
class MonitorMiddleware
{
public function handle($request, Closure $next)
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
/** @var Response $response */
$response = $next($request);
$endTime = microtime(true);
$endMemory = memory_get_usage();
$executionTime = round(($endTime - $startTime) * 1000, 2);
$memoryUsage = round(($endMemory - $startMemory) / 1024 / 1024, 2);
// 记录性能日志
if ($executionTime > 1000) { // 超过1秒记录警告
thinkfacadeLog::warning("慢请求: {$request->path()} - {$executionTime}ms");
}
// 添加响应头
$response->header([
'X-Execution-Time' => $executionTime . 'ms',
'X-Memory-Usage' => $memoryUsage . 'MB'
]);
return $response;
}
}
?>
六、压力测试与部署方案
6.1 使用Apache Bench进行压力测试
# 模拟1000个并发,总共10000个请求
ab -n 10000 -c 1000 http://localhost/seckill/seckill/1
# 测试结果分析指标:
# - Requests per second: 每秒处理请求数
# - Time per request: 每个请求平均处理时间
# - Failed requests: 失败请求数
# - 95%响应时间
6.2 部署架构建议
- 负载均衡:Nginx + Keepalived
- 应用服务器:多台PHP-FPM服务器
- 缓存层:Redis哨兵模式或集群
- 数据库:MySQL主从复制 + 分库分表
- 队列服务:独立Redis实例或RabbitMQ
- 监控告警:Prometheus + Alertmanager
七、总结与最佳实践
通过本文的完整实现,我们基于ThinkPHP 8.0构建了一个高性能的秒杀系统。关键设计要点包括:
核心设计原则:
- 流量削峰:通过队列异步处理订单,平滑系统压力
- 库存预热:活动开始前将库存加载到Redis
- 防止超卖:Redis原子操作 + 数据库事务保证
- 限流降级:用户频率限制 + 系统熔断机制
- 监控预警:全链路性能监控和告警
进一步优化方向:
- 引入CDN加速静态资源
- 实现灰度发布和蓝绿部署
- 添加分布式追踪系统
- 优化数据库索引和查询性能
- 实现自动扩缩容机制
本方案已在生产环境验证,能够支撑万级并发秒杀场景。开发者可以根据实际业务需求进行调整和扩展,ThinkPHP框架的灵活性和高性能为复杂业务场景提供了坚实基础。

