PHP利用flock解决高并发下单库存超卖

顾言
顾言
发布时间:2026-10-14 | 4人浏览过
原创

在高并发的电商抢购或秒杀活动中,多线程或多进程同时读取并更新数据库会导致“超卖”现象(库存变为负数)。本文介绍一种基于PHP `flock`函数的轻量级解决方案,通过文件排他锁串行化处理下单请求,在保证数据一致性的同时兼顾系统稳定性。

问题分析:为什么会出现库存超卖?

假设购买商品的完整流程涉及两条主要SQL语句:

  1. sql1: 查询当前商品库存。
  2. sql2: 执行库存扣减操作(如 UPDATE table SET stock = stock - 1)。

在没有并发控制的情况下,若同时有两个用户发起请求且剩余库存仅剩1件:

  • 两个请求同时执行 sql1,查到的库存均大于0。
  • 随后两个请求都成功执行 sql2。
  • 最终结果:库存从1变为-1,产生超卖事故。

解决方案:文件排他锁机制

解决此类并发问题的常见思路包括乐观锁、队列串行化以及原子性SQL判断等。其中,flock 文件锁是一种在单服务器或多个服务器共享存储环境下,控制进程/线程访问代码块的有效手段。

其核心逻辑是:在处理订单前,先尝试获取锁;如果锁定成功,则执行下单逻辑;否则根据策略拒绝请求或等待。

第1步:准备锁文件路径

在脚本目录下创建一个用于同步的信号文件,例如 lock.txt。该文件本身只作为互斥信号,无需维护内容。

第2步:编写阻塞模式下的锁控制逻辑

适用场景:希望请求排队处理,不直接拒绝用户,但用户可能需要较长时间等待结果。

在此模式下,如果锁已被其他进程占用,当前进程将一直等待直到获取到锁。这可能导致大量请求堆积,消耗内存和连接资源,需谨慎使用。

<?php
// 打开锁文件
$fp = fopen("lock.txt", "w+");

// 尝试获取独占锁(阻塞模式),直到获取成功
if (flock($fp, LOCK_EX)) {
    // ===========================
    // 关键代码区域:处理下单业务逻辑
    // 此处应包含生成订单号、写入数据库库存扣减等操作
    // ===========================

    // 业务处理完毕后,释放锁
    flock($fp, LOCK_UN);
}

// 关闭文件句柄
fclose($fp);
?>

第3步:编写非阻塞模式下的锁控制逻辑

适用场景:追求响应速度,如果无法立即获得锁,直接告知用户“系统繁忙”,保护服务器不被瞬时海量请求压垮。

通过在 flock 中增加 LOCK_NB 标志位实现非阻塞。如果返回 false,说明锁被占用,程序跳过业务逻辑直接报错退出。

<?php
$fp = fopen("lock.txt", "w+");

// 尝试获取独占锁,添加 LOCK_NB 实现非阻塞
if (flock($fp, LOCK_EX | LOCK_NB)) {
    // 获取锁成功,执行下单逻辑
    // ... 业务代码 ...
    flock($fp, LOCK_UN);
} else {
    // 获取锁失败,提示用户
    echo "系统繁忙,请稍后再试";
    // 必须关闭句柄,避免资源泄露
}

fclose($fp);
?>

实施要点与风险提示

1. 必须正确释放和关闭资源

在 try-catch 结构或正常的流程结束处,务必调用 flock($fp, LOCK_UN) 释放锁,并在最后调用 fclose($fp)。未正确释放锁会导致后续请求永远无法获取资源,造成服务挂起。

2. 性能考量

flock 本质上是将并发转化为串行。对于极高并发的秒杀场景,单文件的锁竞争会成为瓶颈,建议结合 Redis Lua 脚本或数据库乐观锁实现更复杂的分布式限流。但在中小规模活动或单机部署中,这是一种简单有效的方案。

3. 文件权限检查

确保运行PHP进程的Web服务器用户(如 www-data, apache, nginx)具有对 lock.txt 文件的读写权限,否则 fopen 或 flock 会静默失败或引发警告。

总结

利用PHP的 flock 函数进行文件排他锁,是解决本地进程并发下库存超卖的一种直观且易实现的方案。开发时需区分“阻塞”与“非阻塞”两种模式以匹配业务需求,并严格管理锁的释放,以避免死锁。

以上就是使用PHP flock解决高并发入库导致库存负数的详细内容,更多关于PHP并发优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表