当前位置:首页 > PHP

php 实现抢购功能

2026-02-16 21:58:06PHP

数据库设计

使用事务和锁机制确保数据一致性。创建商品表时需包含库存字段(stock)并设置乐观锁(version字段)或悲观锁。

CREATE TABLE `seckill_goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `stock` int(11) NOT NULL COMMENT '库存',
  `version` int(11) DEFAULT 0 COMMENT '乐观锁版本号',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

乐观锁实现

通过版本号控制并发,更新时校验版本号是否变化。适合冲突较少的场景。

$pdo->beginTransaction();
try {
    $stmt = $pdo->prepare("SELECT stock, version FROM seckill_goods WHERE id = ? FOR UPDATE");
    $stmt->execute([$goodsId]);
    $goods = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($goods['stock'] > 0) {
        $update = $pdo->prepare("UPDATE seckill_goods SET stock = stock - 1, version = version + 1 
                               WHERE id = ? AND version = ?");
        $affected = $update->execute([$goodsId, $goods['version']]);

        if ($affected) {
            // 生成订单逻辑
            $pdo->commit();
            return true;
        }
    }
    $pdo->rollBack();
    return false;
} catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
}

悲观锁实现

使用SELECT...FOR UPDATE锁定记录,适合高并发场景。需注意死锁风险。

$pdo->beginTransaction();
try {
    $stmt = $pdo->prepare("SELECT stock FROM seckill_goods WHERE id = ? FOR UPDATE");
    $stmt->execute([$goodsId]);
    $stock = $stmt->fetchColumn();

    if ($stock > 0) {
        $update = $pdo->prepare("UPDATE seckill_goods SET stock = stock - 1 WHERE id = ?");
        $update->execute([$goodsId]);

        // 生成订单逻辑
        $pdo->commit();
        return true;
    }
    $pdo->rollBack();
    return false;
} catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
}

Redis队列方案

使用Redis的原子操作减轻数据库压力,通过LIST结构实现排队。

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

// 预加载库存到Redis
$redis->set("goods_stock_{$goodsId}", 100);

// 抢购逻辑
$remaining = $redis->decr("goods_stock_{$goodsId}");
if ($remaining >= 0) {
    // 订单入队列
    $redis->lPush("order_queue", json_encode([
        'user_id' => $userId,
        'goods_id' => $goodsId
    ]));
    return true;
} else {
    $redis->incr("goods_stock_{$goodsId}"); // 恢复库存
    return false;
}

限流措施

采用令牌桶算法控制请求速率,防止系统过载。

$rateLimiter = new RedisRateLimiter($redis);
$key = "user_limit_{$userId}";

if (!$rateLimiter->consume($key, 10, 60)) { // 60秒内最多10次
    header('HTTP/1.1 429 Too Many Requests');
    exit;
}

前端优化

使用倒计时同步和按钮禁用防止重复提交。活动开始前禁用提交按钮,通过服务端时间校准倒计时。

function syncCountdown() {
    fetch('/api/server_time').then(res => res.json()).then(data => {
        const remainMs = new Date(data.start_time) - Date.now();
        if (remainMs > 0) {
            disableSubmitButton();
            setTimeout(enableSubmitButton, remainMs);
        }
    });
}

库存预热

活动前将库存加载到内存数据库,采用分段锁提升并发性能。将商品库存拆分为多个子库存单元,减少锁竞争。

// 初始化10个子库存
for ($i=0; $i<10; $i++) {
    $redis->set("sub_stock_{$goodsId}_{$i}", 10);
}

// 随机选择子库存扣减
$slot = mt_rand(0, 9);
$subKey = "sub_stock_{$goodsId}_{$slot}";
if ($redis->decr($subKey) >= 0) {
    // 扣减成功
}

异步处理

使用消息队列解耦抢购与订单创建流程,提升系统响应速度。

$message = [
    'user_id' => $userId,
    'goods_id' => $goodsId,
    'timestamp' => time()
];
$redis->lPush('seckill_orders', json_encode($message));

// 后台Worker处理订单
while (true) {
    $order = $redis->rPop('seckill_orders');
    if ($order) {
        processOrder(json_decode($order, true));
    }
    usleep(100000); // 100ms间隔
}

php 实现抢购功能

标签: 功能php
分享给朋友:

相关文章

vue功能实现

vue功能实现

Vue 功能实现指南 Vue.js 是一个渐进式 JavaScript 框架,广泛用于构建用户界面。以下是 Vue 功能的常见实现方法。 数据绑定 Vue 的核心功能之一是数据绑定,通过 v-mod…

vue实现tab功能

vue实现tab功能

Vue 实现 Tab 功能的方法 使用动态组件和 v-if 指令 通过 v-if 或 v-show 控制不同 Tab 内容的显示与隐藏。这种方法适合简单的 Tab 切换需求。 <templa…

php实现搜索功能

php实现搜索功能

实现基础搜索功能 使用PHP和MySQL实现基础的搜索功能需要结合表单提交与数据库查询。创建一个HTML表单用于接收用户输入的搜索关键词,通过PHP处理表单数据并查询数据库。 // 搜索表单 (H…

vue实现聊天功能

vue实现聊天功能

Vue实现聊天功能的方法 使用WebSocket实现实时通信 安装WebSocket客户端库如socket.io-client,在Vue组件中建立连接。创建一个消息列表和数据输入框,通过WebSock…

vue实现账号注册功能

vue实现账号注册功能

实现账号注册功能 在Vue中实现账号注册功能通常需要结合前端表单和后端API交互。以下是一个完整的实现方案: 创建注册表单组件 新建一个Register.vue组件,包含基本的注册表单字段:…

php实现搜索功能

php实现搜索功能

实现基本的搜索功能 在PHP中实现搜索功能通常涉及数据库查询。以下是一个简单的实现方式,假设使用MySQL数据库: <?php // 连接数据库 $conn = new mysqli('lo…