php并发请求导致数据重复提交,核心在于请求到达服务端时状态未锁定或检查与写入非原子操作。解决方向是引入分布式锁或数据库唯一约束,适用条件为高并发场景且允许短暂等待或重试。改代码处为业务逻辑入口,需先获取锁再执行,运行后成功表现为仅一条记录入库,失败查锁超时日志或死锁报错。

Redis分布式锁实现
使用Redis SETNX命令实现互斥锁,确保同一时刻只有一个请求能处理该业务。代码中需包含连接初始化、加锁判断、业务执行及解锁流程,注意异常时的锁释放。

<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$key = 'lock_order_' . $_GET['order_id'];
$isLocked = $redis->set($key, 'locked', ['nx', 'ex' => 10]);
if (!$isLocked) {
echo json_encode(['status' => 'error', 'msg' => 'Request too fast']);
exit;
}
try {
// 模拟业务处理
sleep(2);
echo json_encode(['status' => 'success', 'data' => 'Order created']);
} finally {
$redis->del($key);
}
?>
结果输出显示成功创建订单,若快速刷新页面,第二次请求将直接返回“Request too fast”,证明锁生效。未捕获异常时finally块确保锁被释放,避免死锁。
立即学习“PHP免费学习笔记(深入)”;
数据库唯一索引校验
在应用层锁之外,数据库层面必须设置唯一索引作为最终防线。即使锁失效,插入重复数据也会触发异常。关键分支在于捕获PDOException并区分唯一冲突与其他错误。

<?php
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', 'user', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$stmt = $pdo->prepare("INSERT INTO orders (order_id, amount) VALUES (:oid, :amt)");
$stmt->execute([':oid' => $_GET['order_id'], ':amt' => 100]);
echo "Insert success";
} catch (PDOException $e) {
if ($e->getCode() == '23000') {
echo "Duplicate entry ignored";
} else {
throw $e;
}
}
?
此段代码演示了当Redis锁失效时,数据库唯一索引拦截重复插入。捕获特定错误码23000可静默处理重复请求,其他错误则重新抛出,保证系统健壮性。
验证与边界测试
通过ab工具模拟并发请求验证效果。发送50个相同参数请求,观察日志中成功次数应为1次。边界情况包括Redis宕机时降级为数据库校验,以及锁过期时间设置过短导致正常业务被误杀。实际部署时需监控锁命中率及数据库慢查询。



















