
本文详解如何使用 php + pdo 实现“若记录存在则累加 quantity,否则插入新记录”的原子化操作,并提供安全、可复用的代码示例与最佳实践。
本文详解如何使用 php + pdo 实现“若记录存在则累加 quantity,否则插入新记录”的原子化操作,并提供安全、可复用的代码示例与最佳实践。
在电商库存管理、购物车计数或用户行为统计等场景中,常需对某条记录的 quantity 字段进行“存在即累加,不存在即初始化”的操作。直接依赖 SELECT + INSERT/UPDATE 的两步逻辑虽直观,但存在并发风险(如两个请求同时读到“不存在”,导致重复插入)。因此,推荐优先使用 MySQL 原生的 INSERT ... ON DUPLICATE KEY UPDATE 语句,它以单条原子 SQL 完成判断与更新,高效且线程安全。
✅ 推荐方案:INSERT ... ON DUPLICATE KEY UPDATE
前提:确保 id 字段已设置为主键(PRIMARY KEY)或唯一索引(UNIQUE INDEX),否则 ON DUPLICATE KEY 不生效。
<?php try {
$pdo = new PDO("mysql:host=localhost;dbname=test", $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
$id = 123;
$increment = 5; // 待累加的数量
$sql = "INSERT INTO products (id, quantity) VALUES (?, ?)
ON DUPLICATE KEY UPDATE quantity = quantity + VALUES(quantity)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$id, $increment]);
echo "操作成功:ID {$id} 的 quantity 已更新或新增。";
} catch (PDOException $e) {
error_log("SQL 错误: " . $e->getMessage());
throw new Exception("库存更新失败,请稍后重试");
}
?>
? 说明:VALUES(quantity) 指代 INSERT 子句中对应位置的值(即 $increment),因此 quantity = quantity + VALUES(quantity) 等价于“原值 + 新增值”。
⚠️ 注意事项与最佳实践
- 必须有唯一约束:id 列务必定义为 PRIMARY KEY 或 UNIQUE,否则 ON DUPLICATE KEY 将被忽略。
- 避免 SQL 注入:始终使用预处理语句(prepare + execute),禁止字符串拼接。
-
事务保障(高并发场景):若业务逻辑涉及多表联动(如同时扣减库存并生成订单),建议包裹在事务中:
$pdo->beginTransaction(); try { $pdo->prepare("INSERT INTO products (...) ... ON DUPLICATE KEY UPDATE ...")->execute([...]); $pdo->prepare("INSERT INTO logs (...)")->execute([...]); $pdo->commit(); } catch (Exception $e) { $pdo->rollback(); throw $e; } - 不推荐“先查后改”模式:原始答案中的 SELECT → IF → INSERT/UPDATE 方案在高并发下易引发竞态条件(race condition),仅作教学理解,生产环境应避免。
? 总结
使用 INSERT ... ON DUPLICATE KEY UPDATE 是实现“存在则累加、不存在则插入”的标准、高效、安全方式。配合 PDO 预处理与合理索引设计,可彻底规避并发写入问题。开发者应优先掌握该语法,而非依赖应用层逻辑模拟数据库行为。











