当前位置:首页 > 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) {
    // 扣减成功
}

异步处理

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

php 实现抢购功能

$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
分享给朋友:

相关文章

vue 实现收藏功能

vue 实现收藏功能

实现收藏功能的基本思路 在Vue中实现收藏功能通常涉及前端交互与后端数据存储的结合。核心逻辑包括:用户点击收藏按钮时切换状态,并通过API将状态同步到后端数据库。 前端组件实现 创建收藏按钮组件,使…

vue实现名字查找功能

vue实现名字查找功能

实现名字查找功能 在Vue中实现名字查找功能,可以通过多种方式完成。以下是几种常见的实现方法: 使用v-model和computed属性 创建一个输入框,使用v-model绑定输入的值,通过comp…

vue功能实现

vue功能实现

Vue 功能实现指南 Vue.js 是一个流行的前端框架,用于构建用户界面和单页应用。以下是 Vue 功能实现的关键方法和技术。 数据绑定与响应式 Vue 的核心特性是数据绑定和响应式系统。通过 d…

php验证码实现

php验证码实现

验证码实现方法 在PHP中实现验证码功能通常涉及生成随机字符串或数字,将其转换为图像,并通过会话(Session)进行验证。以下是几种常见的实现方式: 使用GD库生成图像验证码 GD库是PHP内置的…

uniapp实现支付功能

uniapp实现支付功能

支付功能实现概述 在UniApp中实现支付功能通常需要对接第三方支付平台(如微信支付、支付宝支付等)。以下是基于微信支付和支付宝支付的通用实现流程。 微信支付实现步骤 1. 配置支付权限 在微信开放…

uniapp实现选项卡功能

uniapp实现选项卡功能

实现选项卡功能的基本方法 在uniapp中实现选项卡功能通常需要使用uni-segmented-control组件或自定义样式结合swiper组件。以下是两种常见实现方式: 使用uni-segmen…