php实现乐观锁
乐观锁的基本概念
乐观锁是一种并发控制机制,假设多用户并发访问时不会产生冲突,只在数据提交时检查是否被其他事务修改过。通常通过版本号或时间戳实现。
实现方法
基于版本号的乐观锁
在数据表中添加一个version字段,每次更新时检查版本号是否匹配:
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100),
stock INT,
version INT DEFAULT 0
);
PHP代码示例
通过版本号实现更新冲突检测:
// 假设从数据库获取原始数据
$productId = 1;
$originalVersion = 5;
$newStock = 100;
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $pdo->prepare(
"UPDATE products SET stock = :stock, version = version + 1
WHERE id = :id AND version = :version"
);
$stmt->bindParam(':stock', $newStock);
$stmt->bindParam(':id', $productId);
$stmt->bindParam(':version', $originalVersion);
$affectedRows = $stmt->execute();
if ($affectedRows === 0) {
throw new Exception('更新失败,数据已被其他操作修改');
}
基于时间戳的乐观锁
使用updated_at字段替代版本号:
ALTER TABLE products ADD COLUMN updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
PHP代码示例
$productId = 1;
$originalTimestamp = '2023-01-01 12:00:00';
$stmt = $pdo->prepare(
"UPDATE products SET stock = :stock
WHERE id = :id AND updated_at = :timestamp"
);
$stmt->bindParam(':stock', $newStock);
$stmt->bindParam(':id', $productId);
$stmt->bindParam(':timestamp', $originalTimestamp);
注意事项
- 需要确保版本号或时间戳的查询和更新是原子操作
- 高并发场景下可能产生大量失败操作,需配合重试机制
- 不适合强一致性要求的场景(如金融系统)
扩展方案
结合Redis实现分布式乐观锁:

$redis = new Redis();
$redis->connect('127.0.0.1');
$lockKey = "product_lock_{$productId}";
// 设置NX锁(仅当key不存在时生效)
$locked = $redis->set($lockKey, 1, ['NX', 'EX' => 10]);
if (!$locked) {
throw new Exception('系统繁忙,请重试');
}
try {
// 执行数据库操作
} finally {
$redis->del($lockKey);
}






