PHP Slim 3实现端点单请求处理与请求排队方案
解决Slim 3端点并发请求导致的重复插入问题
问题根源
并发请求时,多个请求同时执行SELECT查询,都通过了自定义逻辑判断,随后同时执行INSERT操作,触发数据库唯一键约束,抛出Integrity constraint violation: 1062 Duplicate entry错误。要实现端点串行处理请求,可从数据库层面或应用层面入手。
方案一:数据库事务+行级锁(推荐)
利用数据库事务和SELECT ... FOR UPDATE锁定查询到的行,确保同一时间只有一个请求能获取该行的操作权限,从根源避免并发冲突。
修改后的代码示例:
function addClient($request) { $client = json_decode($request->getBody()); // 给查询语句加上FOR UPDATE,锁定结果行 $sql = 'SELECT `unique_column` FROM your_table WHERE [查询条件] FOR UPDATE'; try { $db = getConnection(); $db->beginTransaction(); // 开启事务 $stmt = $db->prepare($sql); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC); if ($row && [some logic]) { // 确保行存在且满足自定义逻辑 $get_card_query = "INSERT INTO another_table (target_column) VALUES (?)"; $stmt = $db->prepare($get_card_query); $stmt->bindParam(1, $row['unique_column']); $stmt->execute(); } $db->commit(); // 提交事务,释放锁 $db = null; } catch(PDOException $e){ if (isset($db)) { $db->rollBack(); // 出错时回滚事务 } echo '{"error":{"text":'. $e->getMessage() .'}}'; } }
- 核心逻辑:
FOR UPDATE会锁定查询到的行,直到事务提交或回滚;其他请求会等待锁释放后再执行查询,从数据库层面保证操作的串行性。
方案二:应用级文件锁(实现请求排队)
如果需要在应用层面控制整个端点的串行处理,可使用文件锁机制,确保同一时间只有一个请求进入业务逻辑。
代码示例:
function addClient($request) { // 指定锁文件路径,需确保所有进程有读写权限 $lockFile = __DIR__ . '/add_client_lock.txt'; $lockHandle = fopen($lockFile, 'w'); // 阻塞等待锁,直到获取到权限 if (!flock($lockHandle, LOCK_EX)) { echo '{"error":{"text":"当前请求过多,请稍后重试"}}'; fclose($lockHandle); return; } $client = json_decode($request->getBody()); $sql = 'select unique column'; try { $db = getConnection(); $stmt = $db->prepare($sql); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC); if(some logic) { $get_card_query = "insert into another_table (target_column) values (?)"; $stmt = $db->prepare($get_card_query); $stmt->bindParam(1, $row[0]); $stmt->execute(); } $db = null; } catch(PDOException $e){ echo '{"error":{"text":'. $e->getMessage() .'}}'; } finally { // 必须释放锁,避免死锁 flock($lockHandle, LOCK_UN); fclose($lockHandle); } }
- 核心逻辑:
LOCK_EX是排他锁,同一时间只有一个进程能获取;未获取到锁的请求会阻塞等待,直到锁被释放,实现请求排队处理。
方案三:修改INSERT语句避免冲突
如果业务允许不严格串行,仅需避免报错,可直接修改INSERT语句,利用数据库的冲突处理机制:
// 替换原INSERT语句为: $get_card_query = "INSERT INTO another_table (target_column) VALUES (?) ON DUPLICATE KEY UPDATE target_column = VALUES(target_column)";
- 逻辑说明:当出现重复插入时,会执行更新操作(可根据业务需求调整为不做任何操作),不会抛出错误,但无法保证请求的串行处理顺序。
内容的提问来源于stack exchange,提问作者ibrahim abdulkhaleq
相关产品推荐
相关产品推荐

