导读:本期聚焦于小伙伴创作的《如何使用PHP处理数据库的乐观锁并发控制,在Update语句中增加版本号?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何使用PHP处理数据库的乐观锁并发控制,在Update语句中增加版本号?》有用,将其分享出去将是对创作者最好的鼓励。

在高并发的业务场景中,比如商品库存扣减、订单状态更新等操作,多个请求同时修改同一条数据很容易出现数据覆盖、库存超扣等问题,乐观锁就是解决这类并发问题的常用方案,其中通过Update语句增加版本号是最主流的实现方式。

如何使用PHP处理数据库的乐观锁并发控制,在Update语句中增加版本号?

乐观锁与版本号机制的核心原理

乐观锁的核心思想是假设并发冲突发生的概率较低,因此不会在读取数据时就加锁,而是在更新数据时判断数据是否被其他事务修改过。版本号机制是乐观锁的常见实现形式,具体逻辑如下:

  • 数据表中新增一个version字段,初始值为0或者1
  • 读取数据时,同时获取当前数据的version
  • 更新数据时,在Update语句的Where条件中加入version等于之前读取到的版本号,同时把version值加1
  • 如果Update语句返回的影响行数为1,说明更新成功,没有其他事务修改过该数据;如果影响行数为0,说明数据已经被其他事务修改,当前更新失败,需要进行重试或者提示用户

数据库表结构设计

我们以商品库存表为例,设计包含版本号字段的表结构,这里使用MySQL数据库作为示例:

-- 创建商品库存表,包含版本号字段
CREATE TABLE `product_stock` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_name` varchar(50) NOT NULL COMMENT '商品名称',
  `stock_num` int(11) NOT NULL COMMENT '库存数量',
  `version` int(11) NOT NULL DEFAULT '1' COMMENT '版本号,用于乐观锁控制',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品库存表';

-- 插入一条测试数据
INSERT INTO `product_stock` (`product_name`, `stock_num`, `version`) VALUES ('测试商品', 100, 1);

PHP实现乐观锁的完整代码

我们使用PDO扩展连接数据库,实现商品库存扣减的乐观锁控制逻辑,完整代码如下:

<?php
// 数据库配置
$dbConfig = [
    'host' => '127.0.0.1',
    'port' => 3306,
    'dbname' => 'test_db',
    'username' => 'root',
    'password' => '123456',
    'charset' => 'utf8mb4'
];

// 连接数据库
try {
    $dsn = "mysql:host={$dbConfig['host']};port={$dbConfig['port']};dbname={$dbConfig['dbname']};charset={$dbConfig['charset']}";
    $pdo = new PDO($dsn, $dbConfig['username'], $dbConfig['password']);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die("数据库连接失败:" . $e->getMessage());
}

// 商品ID,假设要扣减ID为1的商品库存
$productId = 1;
// 要扣减的库存数量
$deductNum = 1;

// 重试次数,最多重试3次
$retryTimes = 3;
$success = false;

for ($i = 0; $i < $retryTimes; $i++) {
    // 1. 读取当前商品的库存和版本号
    $querySql = "SELECT `stock_num`, `version` FROM `product_stock` WHERE `id` = :product_id";
    $stmt = $pdo->prepare($querySql);
    $stmt->execute([':product_id' => $productId]);
    $productInfo = $stmt->fetch(PDO::FETCH_ASSOC);

    // 判断商品是否存在
    if (empty($productInfo)) {
        die("商品不存在");
    }

    // 判断库存是否足够
    if ($productInfo['stock_num'] < $deductNum) {
        die("商品库存不足");
    }

    // 2. 执行带版本号的Update语句
    $updateSql = "UPDATE `product_stock` 
                  SET `stock_num` = `stock_num` - :deduct_num, 
                      `version` = `version` + 1 
                  WHERE `id` = :product_id 
                    AND `version` = :old_version";
    $updateStmt = $pdo->prepare($updateSql);
    $updateParams = [
        ':deduct_num' => $deductNum,
        ':product_id' => $productId,
        ':old_version' => $productInfo['version']
    ];
    $updateStmt->execute($updateParams);
    $affectedRows = $updateStmt->rowCount();

    // 3. 判断更新是否成功
    if ($affectedRows == 1) {
        $success = true;
        echo "库存扣减成功,当前剩余库存:" . ($productInfo['stock_num'] - $deductNum) . PHP_EOL;
        break;
    } else {
        echo "第" . ($i + 1) . "次扣减失败,数据已被其他请求修改,准备重试" . PHP_EOL;
        // 重试前可以短暂休眠,避免无效重试
        usleep(100000); // 休眠100毫秒
    }
}

if (!$success) {
    echo "库存扣减失败,重试次数已用完" . PHP_EOL;
}

并发场景下的验证方法

我们可以通过模拟并发请求来验证乐观锁的效果,比如同时发起10个请求扣减同一商品的库存,每个请求扣减1件,初始库存为100,最终库存应该为90,不会出现超扣的情况。

如果是本地测试,可以使用Apache Bench工具发起并发请求,命令示例如下:

# 发起10个并发请求,总共请求10次,调用库存扣减的PHP接口
ab -n 10 -c 10 http://127.0.0.1/stock_deduct.php

执行完成后查询数据库的商品库存,会发现库存正确扣减,没有出现负数或者扣减数量不对的情况,说明乐观锁生效。

注意事项与适用场景

适用场景

  • 并发冲突概率较低的场景,比如大部分业务场景中,同一数据同时被修改的概率不高
  • 读多写少的业务场景,乐观锁不会阻塞读操作,性能相对更好

注意事项

  • 重试次数需要合理设置,避免无限重试导致请求阻塞,一般设置3到5次即可
  • 版本号字段建议使用整型,自增效率高,也可以根据业务需求使用时间戳作为版本号,但要注意时间戳的精度问题
  • 如果业务场景并发冲突非常高,乐观锁的重试成本会很高,此时更适合使用悲观锁
  • Update语句中版本号的判断和更新要放在同一个语句中,避免先查询再更新的非原子操作问题
需要注意的是,乐观锁的版本号机制只适用于单数据库实例的场景,如果是分库分表或者分布式数据库场景,需要结合分布式锁或者其他分布式乐观锁方案实现。

PHP乐观锁数据库并发控制Update版本号修改时间:2026-06-13 04:51:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。