php 实现事务锁
PHP 实现事务锁的方法
在 PHP 中,事务锁通常用于确保数据库操作的原子性和一致性,避免并发操作导致的数据冲突。以下是几种常见的实现方法:

使用 MySQL 事务与行级锁
通过 MySQL 的 InnoDB 引擎支持的事务和行级锁机制,可以实现事务锁。

$pdo = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$pdo->beginTransaction();
try {
// 锁定某一行数据(FOR UPDATE 表示排他锁)
$stmt = $pdo->prepare('SELECT * FROM accounts WHERE id = ? FOR UPDATE');
$stmt->execute([$accountId]);
$account = $stmt->fetch();
// 执行更新操作
$updateStmt = $pdo->prepare('UPDATE accounts SET balance = ? WHERE id = ?');
$updateStmt->execute([$newBalance, $accountId]);
// 提交事务
$pdo->commit();
} catch (Exception $e) {
// 回滚事务
$pdo->rollBack();
throw $e;
}
FOR UPDATE:对查询的行加排他锁,其他事务无法修改或加锁。LOCK IN SHARE MODE:共享锁,允许其他事务读取但不能修改。
使用 Redis 分布式锁
在分布式环境中,可以使用 Redis 实现事务锁,避免多服务器并发问题。
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$lockKey = 'account_lock_' . $accountId;
$lockTimeout = 10; // 锁超时时间(秒)
// 尝试加锁(SETNX + EXPIRE)
$locked = $redis->set($lockKey, 1, ['nx', 'ex' => $lockTimeout]);
if ($locked) {
try {
// 执行事务操作
$pdo->beginTransaction();
// ... 数据库操作
$pdo->commit();
} finally {
// 释放锁
$redis->del($lockKey);
}
} else {
throw new Exception('获取锁失败,请重试');
}
set参数:nx表示仅当键不存在时设置,ex设置超时时间。- 锁释放:必须确保锁最终被释放,避免死锁。
使用文件锁(flock)
对于单机环境,可以通过文件锁实现简单的互斥锁。
$lockFile = '/tmp/account_lock_' . $accountId;
$fp = fopen($lockFile, 'w+');
if (flock($fp, LOCK_EX)) { // 获取排他锁
try {
// 执行事务操作
$pdo->beginTransaction();
// ... 数据库操作
$pdo->commit();
} finally {
// 释放锁
flock($fp, LOCK_UN);
fclose($fp);
}
} else {
throw new Exception('获取锁失败');
}
LOCK_EX:排他锁,其他进程无法获取锁。LOCK_UN:释放锁。
注意事项
- 死锁风险:确保锁最终被释放,避免因异常导致锁未释放。
- 锁粒度:尽量缩小锁的范围(如行级锁而非表锁)。
- 超时机制:为锁设置超时时间,避免长时间阻塞。
- 分布式一致性:在分布式系统中,Redis 或 ZooKeeper 更适用。
根据实际场景选择合适的方法,单机环境可用 MySQL 或文件锁,分布式环境推荐 Redis。






