PHP高性能电商秒杀系统开发实战:从架构设计到防刷策略 | 高并发解决方案

2025-08-11 0 199

千万级并发下的秒杀系统架构设计与实现

一、秒杀系统技术选型

主流秒杀方案性能对比:

技术方案 QPS 开发成本 数据一致性
纯数据库方案 < 1000
Redis+队列 1万-5万 最终一致
Lua脚本+Redis 5万-10万 强一致
分布式锁方案 3万-8万 强一致

二、系统架构设计

1. 分层削峰架构

用户层 → 接入层 → 服务层 → 队列层 → 数据层
    ↑        ↑         ↑         ↑         ↑
限流策略   Nginx分流  业务逻辑   RabbitMQ   Redis集群
            

2. 数据流设计

请求进入 → 风险控制 → 库存预减 → 订单创建 → 支付通知
    ↑          ↑           ↑           ↑           ↑
恶意拦截   黑名单过滤    Redis原子操作   异步队列处理   结果回调

三、核心模块实现

1. 商品库存服务

<?php
class StockService
{
    private $redis;
    private $db;
    
    public function __construct(Redis $redis, PDO $db) {
        $this->redis = $redis;
        $this->db = $db;
    }
    
    /**
     * 初始化秒杀库存
     */
    public function initStock($itemId, $total) {
        $key = "seckill:stock:" . $itemId;
        $this->redis->set($key, $total);
        
        // 预热库存到数据库
        $stmt = $this->db->prepare(
            "INSERT INTO stock(item_id, total, available) 
             VALUES(?, ?, ?) 
             ON DUPLICATE KEY UPDATE total=VALUES(total), available=VALUES(available)"
        );
        $stmt->execute([$itemId, $total, $total]);
    }
    
    /**
     * 原子扣减库存
     */
    public function reduceStock($itemId, $userId) {
        $stockKey = "seckill:stock:" . $itemId;
        $boughtKey = "seckill:user:" . $itemId;
        
        // 使用Lua脚本保证原子性
        $lua = <<<LUA
            local stock = tonumber(redis.call('GET', KEYS[1]))
            if stock <= 0 then
                return 0
            end
            
            local bought = redis.call('SISMEMBER', KEYS[2], ARGV[1])
            if bought == 1 then
                return -1
            end
            
            redis.call('DECR', KEYS[1])
            redis.call('SADD', KEYS[2], ARGV[1])
            return 1
LUA;
        
        $result = $this->redis->eval($lua, [$stockKey, $boughtKey, $userId], 2);
        
        if ($result === 1) {
            // 异步更新数据库
            $this->asyncUpdateDB($itemId);
            return true;
        }
        
        return false;
    }
    
    private function asyncUpdateDB($itemId) {
        $message = json_encode(['item_id' => $itemId, 'type' => 'stock_reduce']);
        $this->redis->lPush('seckill:queue', $message);
    }
}

2. 订单服务

<?php
class OrderService
{
    private $db;
    private $redis;
    
    public function __construct(PDO $db, Redis $redis) {
        $this->db = $db;
        $this->redis = $redis;
    }
    
    /**
     * 创建秒杀订单
     */
    public function createOrder($itemId, $userId) {
        // 检查是否已购买
        if ($this->hasPurchased($itemId, $userId)) {
            throw new Exception('您已经参与过本次秒杀');
        }
        
        // 生成订单号
        $orderNo = $this->generateOrderNo();
        
        try {
            $this->db->beginTransaction();
            
            // 创建订单
            $stmt = $this->db->prepare(
                "INSERT INTO orders(order_no, user_id, item_id, amount, status) 
                 VALUES(?, ?, ?, 1, 'pending')"
            );
            $stmt->execute([$orderNo, $userId, $itemId]);
            
            // 扣减数据库库存
            $stmt = $this->db->prepare(
                "UPDATE stock SET available = available - 1 
                 WHERE item_id = ? AND available > 0"
            );
            $stmt->execute([$itemId]);
            
            if ($stmt->rowCount() == 0) {
                throw new Exception('库存不足');
            }
            
            $this->db->commit();
            
            // 记录用户购买
            $this->recordPurchase($itemId, $userId);
            
            return $orderNo;
        } catch (Exception $e) {
            $this->db->rollBack();
            throw $e;
        }
    }
    
    private function generateOrderNo() {
        return date('YmdHis') . str_pad(mt_rand(0, 9999), 4, '0', STR_PAD_LEFT);
    }
    
    private function hasPurchased($itemId, $userId) {
        $key = "seckill:user:" . $itemId;
        return $this->redis->sIsMember($key, $userId);
    }
    
    private function recordPurchase($itemId, $userId) {
        $key = "seckill:user:" . $itemId;
        $this->redis->sAdd($key, $userId);
    }
}

四、高级功能实现

1. 防刷限流策略

<?php
class AntiSpamService
{
    private $redis;
    
    public function __construct(Redis $redis) {
        $this->redis = $redis;
    }
    
    /**
     * IP限流检查
     */
    public function checkIpLimit($ip, $itemId, $limit = 10) {
        $key = "seckill:ip_limit:" . $itemId . ":" . $ip;
        $count = $this->redis->incr($key);
        
        if ($count == 1) {
            $this->redis->expire($key, 60);
        }
        
        return $count > $limit;
    }
    
    /**
     * 用户行为分析
     */
    public function analyzeUserBehavior($userId, $itemId) {
        $key = "seckill:user_behavior:" . $userId;
        $data = [
            'ip' => $_SERVER['REMOTE_ADDR'],
            'user_agent' => $_SERVER['HTTP_USER_AGENT'],
            'item_id' => $itemId,
            'time' => time()
        ];
        
        $this->redis->lPush($key, json_encode($data));
        $this->redis->lTrim($key, 0, 49);
        
        // 分析最近50次行为
        $behaviors = $this->redis->lRange($key, 0, -1);
        $analysis = $this->doAnalysis($behaviors);
        
        if ($analysis['risk_score'] > 80) {
            $this->addToBlacklist($userId);
        }
    }
    
    private function doAnalysis($behaviors) {
        // 简化的风险分析逻辑
        $itemIds = [];
        $times = [];
        
        foreach ($behaviors as $behavior) {
            $data = json_decode($behavior, true);
            $itemIds[] = $data['item_id'];
            $times[] = $data['time'];
        }
        
        // 计算请求频率
        $frequency = count($times) / (max($times) - min($times) + 1);
        
        // 计算商品多样性
        $diversity = count(array_unique($itemIds));
        
        // 简单风险评分
        $riskScore = min(100, $frequency * 30 + (1 - $diversity/count($itemIds)) * 70);
        
        return [
            'risk_score' => $riskScore,
            'frequency' => $frequency,
            'diversity' => $diversity
        ];
    }
}

2. 异步任务处理

<?php
class AsyncTaskWorker
{
    private $redis;
    private $db;
    private $running = false;
    
    public function __construct(Redis $redis, PDO $db) {
        $this->redis = $redis;
        $this->db = $db;
    }
    
    public function start() {
        $this->running = true;
        
        while ($this->running) {
            // 从队列获取任务
            $message = $this->redis->brPop('seckill:queue', 30);
            
            if ($message) {
                try {
                    $task = json_decode($message[1], true);
                    $this->processTask($task);
                } catch (Exception $e) {
                    error_log("Task failed: " . $e->getMessage());
                    // 失败任务重试
                    $this->redis->lPush('seckill:queue:retry', $message[1]);
                }
            }
        }
    }
    
    private function processTask($task) {
        switch ($task['type']) {
            case 'stock_reduce':
                $this->updateStock($task['item_id']);
                break;
            case 'order_complete':
                $this->completeOrder($task['order_id']);
                break;
            default:
                throw new Exception("Unknown task type");
        }
    }
    
    private function updateStock($itemId) {
        $stmt = $this->db->prepare(
            "UPDATE stock SET available = available - 1 
             WHERE item_id = ? AND available > 0"
        );
        $stmt->execute([$itemId]);
        
        if ($stmt->rowCount() == 0) {
            throw new Exception("Stock update failed for item: " . $itemId);
        }
    }
    
    private function completeOrder($orderId) {
        $stmt = $this->db->prepare(
            "UPDATE orders SET status = 'completed' 
             WHERE id = ? AND status = 'pending'"
        );
        $stmt->execute([$orderId]);
    }
}

五、性能优化策略

1. OPcache加速

// php.ini 配置
[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1

// 预热脚本
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('/path/to/app')
);

foreach ($files as $file) {
    if ($file->isFile() && $file->getExtension() === 'php') {
        opcache_compile_file($file->getRealPath());
    }
}

2. Swoole协程优化

<?php
use SwooleHttpServer;
use SwooleHttpRequest;
use SwooleHttpResponse;

$server = new Server('0.0.0.0', 9501);

// 配置静态文件处理
$server->set([
    'enable_static_handler' => true,
    'document_root' => '/path/to/public',
    'worker_num' => swoole_cpu_num() * 2,
    'max_coroutine' => 100000
]);

// 秒杀接口
$server->on('request', function (Request $req, Response $res) {
    if ($req->server['request_uri'] === '/seckill') {
        $itemId = $req->get['item_id'] ?? 0;
        $userId = $req->get['user_id'] ?? 0;
        
        // 协程MySQL客户端
        $mysql = new SwooleCoroutineMySQL();
        $mysql->connect([
            'host' => '127.0.0.1',
            'user' => 'root',
            'password' => 'password',
            'database' => 'seckill'
        ]);
        
        // 协程Redis客户端
        $redis = new SwooleCoroutineRedis();
        $redis->connect('127.0.0.1', 6379);
        
        // 执行业务逻辑
        $stockService = new StockService($redis, $mysql);
        $result = $stockService->reduceStock($itemId, $userId);
        
        $res->header('Content-Type', 'application/json');
        $res->end(json_encode(['success' => $result]));
    }
});

$server->start();

六、实战案例:电商秒杀系统

1. 秒杀API接口

<?php
// public/seckill.php
require __DIR__ . '/../vendor/autoload.php';

$config = require __DIR__ . '/../config.php';

// 初始化组件
$redis = new Redis();
$redis->connect($config['redis']['host'], $config['redis']['port']);

$db = new PDO(
    "mysql:host={$config['db']['host']};dbname={$config['db']['database']}",
    $config['db']['user'],
    $config['db']['password']
);

// 创建服务实例
$antiSpam = new AntiSpamService($redis);
$stockService = new StockService($redis, $db);
$orderService = new OrderService($db, $redis);

// 获取参数
$itemId = $_GET['item_id'] ?? 0;
$userId = $_GET['user_id'] ?? 0;
$ip = $_SERVER['REMOTE_ADDR'];

header('Content-Type: application/json');

try {
    // 1. 安全检查
    if ($antiSpam->checkIpLimit($ip, $itemId)) {
        throw new Exception('请求过于频繁,请稍后再试');
    }
    
    // 2. 分析用户行为
    $antiSpam->analyzeUserBehavior($userId, $itemId);
    
    // 3. 扣减库存
    if (!$stockService->reduceStock($itemId, $userId)) {
        throw new Exception('秒杀失败,库存不足');
    }
    
    // 4. 创建订单
    $orderNo = $orderService->createOrder($itemId, $userId);
    
    echo json_encode([
        'success' => true,
        'order_no' => $orderNo
    ]);
} catch (Exception $e) {
    echo json_encode([
        'success' => false,
        'message' => $e->getMessage()
    ]);
}

2. 库存预热脚本

<?php
// scripts/init_stock.php
require __DIR__ . '/../vendor/autoload.php';

$config = require __DIR__ . '/../config.php';

$redis = new Redis();
$redis->connect($config['redis']['host'], $config['redis']['port']);

$db = new PDO(
    "mysql:host={$config['db']['host']};dbname={$config['db']['database']}",
    $config['db']['user'],
    $config['db']['password']
);

$stockService = new StockService($redis, $db);

// 从数据库加载秒杀商品
$stmt = $db->query("SELECT id, stock FROM items WHERE is_seckill = 1");
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($items as $item) {
    echo "初始化商品 {$item['id']} 库存: {$item['stock']}n";
    $stockService->initStock($item['id'], $item['stock']);
}

echo "库存预热完成n";
PHP高性能电商秒杀系统开发实战:从架构设计到防刷策略 | 高并发解决方案
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 php PHP高性能电商秒杀系统开发实战:从架构设计到防刷策略 | 高并发解决方案 https://www.taomawang.com/server/php/798.html

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务