php 实现事务锁
使用 MySQL 事务与行锁
在 PHP 中实现事务锁通常结合 MySQL 的 InnoDB 引擎事务机制。通过 BEGIN 开启事务,配合 SELECT ... FOR UPDATE 锁定特定行,确保操作原子性。
$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$pdo->beginTransaction();
try {
// 锁定指定行(假设锁定ID为1的用户账户)
$stmt = $pdo->prepare("SELECT * FROM accounts WHERE id = 1 FOR UPDATE");
$stmt->execute();
$account = $stmt->fetch(PDO::FETCH_ASSOC);
// 执行更新操作
$update = $pdo->prepare("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
$update->execute();
$pdo->commit(); // 提交事务并释放锁
} catch (Exception $e) {
$pdo->rollBack(); // 回滚并释放锁
echo "Error: " . $e->getMessage();
}
使用 Redis 分布式锁
对于分布式系统,可通过 Redis 的 SETNX(或 Redlock 算法)实现跨进程锁:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$lockKey = 'account_lock_1';
$token = uniqid();
$expire = 3000; // 毫秒
// 尝试获取锁
$locked = $redis->set($lockKey, $token, ['NX', 'PX' => $expire]);
if ($locked) {
try {
// 执行业务逻辑
// ...
// 释放锁(需验证token避免误删)
$script = "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
";
$redis->eval($script, [$lockKey, $token], 1);
} catch (Exception $e) {
$redis->del($lockKey); // 简单处理(生产环境建议用Lua脚本)
}
} else {
echo "获取锁失败,请重试";
}
文件锁(本地单机场景)
通过 flock 实现进程级文件锁:

$fp = fopen("/tmp/lockfile", "w+");
if (flock($fp, LOCK_EX)) { // 排他锁
// 临界区操作
// ...
flock($fp, LOCK_UN); // 释放锁
} else {
echo "无法获取锁";
}
fclose($fp);
注意事项
- 死锁风险:事务锁需设置超时(如 MySQL 的
innodb_lock_wait_timeout) - 锁粒度:行锁比表锁并发性更高,但需索引支持
- 分布式一致性:Redis 锁需处理时钟漂移问题,推荐 Redlock 等成熟方案
- PHP-FPM 场景:确保异常时能正确释放锁(如
register_shutdown_function)






