php 实现乐观锁实现
乐观锁的实现原理
乐观锁基于数据版本(Version)或时间戳(Timestamp)机制实现。在数据读取时不加锁,更新时检查数据是否被其他事务修改。若未被修改,则更新成功;否则根据业务逻辑选择重试或放弃。
基于数据版本的实现
通过为数据表增加一个版本号字段(如 version),在更新时校验版本号是否变化。
数据库表示例:
CREATE TABLE `product` (
`id` int(11) NOT NULL,
`name` varchar(50) NOT NULL,
`stock` int(11) NOT NULL,
`version` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
);
PHP 代码实现:
// 1. 查询数据时获取当前版本号
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $pdo->prepare("SELECT id, stock, version FROM product WHERE id = ?");
$stmt->execute([$productId]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
// 2. 更新数据时校验版本号
$newStock = $product['stock'] - 1;
$stmt = $pdo->prepare(
"UPDATE product SET stock = ?, version = version + 1
WHERE id = ? AND version = ?"
);
$affectedRows = $stmt->execute([$newStock, $productId, $product['version']]);
// 3. 判断是否更新成功
if ($affectedRows > 0) {
echo "更新成功";
} else {
echo "数据已被其他事务修改,请重试";
}
基于时间戳的实现
使用 updated_at 字段记录最后修改时间,更新时校验时间戳是否变化。
数据库表示例:
ALTER TABLE `product` ADD COLUMN `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
PHP 代码实现:
// 1. 查询数据时获取当前时间戳
$stmt = $pdo->prepare("SELECT id, stock, updated_at FROM product WHERE id = ?");
$stmt->execute([$productId]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
// 2. 更新数据时校验时间戳
$newStock = $product['stock'] - 1;
$stmt = $pdo->prepare(
"UPDATE product SET stock = ?, updated_at = NOW()
WHERE id = ? AND updated_at = ?"
);
$affectedRows = $stmt->execute([$newStock, $productId, $product['updated_at']]);
// 3. 处理更新结果(同上)
乐观锁的适用场景
- 读多写少的场景,冲突概率低。
- 业务逻辑允许重试或失败(如库存扣减、抢购系统)。
注意事项:
- 需配合事务使用,确保查询和更新的原子性。
- 高并发场景可能需引入重试机制或队列降级。







