导读:本期聚焦于小伙伴创作的《PHP如何实现数据版本控制?乐观锁和悲观锁怎么实现》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《PHP如何实现数据版本控制?乐观锁和悲观锁怎么实现》有用,将其分享出去将是对创作者最好的鼓励。

在PHP开发的多用户并发场景中,多个请求同时修改同一条数据很容易出现数据覆盖、更新丢失的问题,数据版本控制就是解决这类问题的核心方案,其中乐观锁和悲观锁是最常用的两种实现思路。

PHP如何实现数据版本控制?乐观锁和悲观锁怎么实现

一、悲观锁实现原理与PHP示例

悲观锁的核心思想是假设并发冲突发生的概率很高,在操作数据之前先对数据加锁,确保当前操作完成之前,其他事务无法修改该数据。在MySQL数据库中,我们可以通过FOR UPDATE语法实现行级悲观锁,PHP中结合PDO或mysqli扩展即可完成相关操作。

1. 悲观锁实现步骤

  • 开启数据库事务
  • 查询目标数据时加上FOR UPDATE
  • 执行数据更新操作
  • 提交事务释放锁

2. PHP代码示例

<?php
// 数据库连接配置
$dbHost = '127.0.0.1';
$dbName = 'test_db';
$dbUser = 'root';
$dbPass = '123456';
$dsn = "mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4";
try {
    $pdo = new PDO($dsn, $dbUser, $dbPass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    // 开启事务
    $pdo->beginTransaction();
    // 查询数据并加悲观锁,假设用户id为1
    $userId = 1;
    $querySql = "SELECT id, balance, version FROM user_account WHERE id = ? FOR UPDATE";
    $stmt = $pdo->prepare($querySql);
    $stmt->execute([$userId]);
    $userInfo = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$userInfo) {
        throw new Exception('用户不存在');
    }
    // 模拟业务操作,扣除100元余额
    $newBalance = $userInfo['balance'] - 100;
    if ($newBalance < 0) {
        throw new Exception('余额不足');
    }
    // 更新余额
    $updateSql = "UPDATE user_account SET balance = ? WHERE id = ?";
    $updateStmt = $pdo->prepare($updateSql);
    $updateStmt->execute([$newBalance, $userId]);
    // 提交事务,释放锁
    $pdo->commit();
    echo "余额更新成功,当前余额:$newBalance";
} catch (Exception $e) {
    // 异常时回滚事务
    if (isset($pdo) && $pdo->inTransaction()) {
        $pdo->rollBack();
    }
    echo "操作失败:" . $e->getMessage();
}
?>

需要注意的是,使用悲观锁时必须开启事务,且FOR UPDATE只会锁住查询命中的行,前提是查询条件使用了索引,否则会升级为表锁,影响整体性能。另外,锁的持有时间越长,阻塞其他请求的概率越高,所以要尽量缩短事务的执行时间。

二、乐观锁实现原理与PHP示例

乐观锁的核心思想是假设并发冲突发生的概率很低,不会主动加锁,而是在更新数据时判断数据是否被其他请求修改过,通常通过版本号或者时间戳字段来实现。如果更新时发现版本不匹配,就说明数据已经被修改,需要重试或者提示用户操作失败。

1. 乐观锁实现步骤

  • 给数据表增加一个版本号字段version,或者时间戳字段update_time
  • 查询数据时同时获取当前的版本号
  • 更新数据时,将版本号作为条件,同时版本号加1
  • 判断更新语句的影响行数,如果影响行数为0,说明版本不匹配,更新失败

2. PHP代码示例

<?php
// 数据库连接配置同上,省略重复代码
try {
    $pdo = new PDO($dsn, $dbUser, $dbPass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $userId = 1;
    // 第一次查询获取用户信息和当前版本号
    $querySql = "SELECT id, balance, version FROM user_account WHERE id = ?";
    $stmt = $pdo->prepare($querySql);
    $stmt->execute([$userId]);
    $userInfo = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$userInfo) {
        throw new Exception('用户不存在');
    }
    // 模拟业务操作,扣除100元余额
    $newBalance = $userInfo['balance'] - 100;
    if ($newBalance < 0) {
        throw new Exception('余额不足');
    }
    // 更新数据,条件中带上版本号,同时版本号加1
    $oldVersion = $userInfo['version'];
    $newVersion = $oldVersion + 1;
    $updateSql = "UPDATE user_account SET balance = ?, version = ? WHERE id = ? AND version = ?";
    $updateStmt = $pdo->prepare($updateSql);
    $updateStmt->execute([$newBalance, $newVersion, $userId, $oldVersion]);
    // 判断影响行数
    if ($updateStmt->rowCount() === 0) {
        throw new Exception('数据已被其他用户修改,请重试');
    }
    echo "余额更新成功,当前余额:$newBalance,新版本号:$newVersion";
} catch (Exception $e) {
    echo "操作失败:" . $e->getMessage();
}
?>

如果需要重试机制,可以在更新失败时重新执行查询和更新的逻辑,不过要设置最大重试次数,避免无限重试消耗资源。除了版本号,也可以使用update_time时间戳实现,更新时判断update_time是否和查询时一致即可,原理和版本号完全一致。

三、两种锁的适用场景对比

我们可以通过下面的表格更清晰地对比两种锁的特点和适用场景:

对比项悲观锁乐观锁
加锁方式操作前主动加锁操作前不加锁,更新时校验
适用场景写多读少、并发冲突概率高的场景读多写少、并发冲突概率低的场景
性能影响持有锁期间阻塞其他请求,性能开销较大无阻塞,性能开销小
实现复杂度需要依赖数据库事务和行锁,实现简单需要额外版本字段,更新失败需要处理重试逻辑

在实际开发中,我们可以根据业务的具体并发情况选择合适的方案,比如秒杀场景写操作多、冲突概率高,更适合用悲观锁;而普通的信息编辑场景读多写少,用乐观锁更合适。如果业务并发量特别高,还可以结合Redis等缓存工具实现分布式锁,进一步提升版本控制的可靠性。

PHP乐观锁悲观锁数据版本控制修改时间:2026-07-09 01:24:35

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