
本文详解为何手动检查+更新/插入逻辑会导致数据重复,并提供基于 MySQL INSERT ... ON DUPLICATE KEY UPDATE 的安全、原子化解决方案,同时指出原始代码中的 SQL 注入、函数误用等严重隐患。
本文详解为何手动检查+更新/插入逻辑会导致数据重复,并提供基于 mysql `insert ... on duplicate key update` 的安全、原子化解决方案,同时指出原始代码中的 sql 注入、函数误用等严重隐患。
在 Web 开发中,常见的“先查后插/更”模式(即 SELECT → IF EXISTS THEN UPDATE ELSE INSERT)看似合理,实则存在竞态条件(Race Condition)风险:当两个并发请求几乎同时执行该逻辑时,二者都可能通过 SELECT 判断“记录不存在”,进而各自执行 INSERT,最终导致重复数据——这正是你截图中出现双行同 unique_key 的根本原因。
更关键的是,你的当前代码存在多个高危缺陷,必须立即修正:
? 严重问题清单
-
SQL 注入漏洞:所有
$value直接拼接进 SQL(如"WHERE ".$key." = ".$value),未做任何转义或预处理,攻击者可轻易注入恶意 SQL; -
函数误用:
sql_close()调用了已废弃的mysql_close()(应为mysqli_close()),且连接关闭位置错误(应在所有操作完成后,而非exit后); -
非原子操作:
SELECT和UPDATE/INSERT是两次独立查询,无法保证事务一致性; -
未设置唯一约束:表中
unique_key字段必须添加UNIQUE INDEX或PRIMARY KEY,否则数据库层面无法阻止重复插入。
✅ 正确解法:使用 INSERT ... ON DUPLICATE KEY UPDATE
这是 MySQL 原生支持的原子操作,一行 SQL 即可完成“存在则更新,否则插入”,彻底规避竞态与注入风险:
<?php header('Content-Type: text/plain');
header("Access-Control-Allow-Origin: *");
include "../../functions.php";
$servername = "xxx";
$database = "xxx";
$username = "xxx";
$password = "xxx";
$tabla = "xxx";
$unique_key = $_POST['unique_key'] ?? '';
$nick = $_POST['nick'] ?? '';
$puntos = (int)$_POST['puntos']; // 强制转为整型,防注入
// ✅ 关键:使用预处理语句 + 原子化 UPSERT
$con = mysqli_connect($servername, $username, $password, $database);
if (!$con) {
die("Connection failed: " . mysqli_connect_error());
}
// 确保 unique_key 字段有 UNIQUE 约束(执行一次即可)
// ALTER TABLE `xxx` ADD UNIQUE (`unique_key`);
$stmt = $con->prepare("INSERT INTO `$tabla` (`unique_key`, `nick`, `sc`) VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE `nick` = VALUES(`nick`), `sc` = VALUES(`sc`)");
$stmt->bind_param("sii", $unique_key, $nick, $puntos);
if ($stmt->execute()) {
echo $stmt->affected_rows === 1 ? "OK INSERT" : "OK UPDATE";
} else {
echo "ERROR: " . $stmt->error;
}
$stmt->close();
mysqli_close($con);
?>
⚠️ 注意事项
-
数据库前提:务必为
unique_key字段添加唯一索引:ALTER TABLE `your_table_name` ADD UNIQUE INDEX `idx_unique_key` (`unique_key`);
-
参数绑定:
bind_param("sii", ...)中s表示字符串、i表示整型,自动转义,杜绝 SQL 注入; -
VALUES(col)语法:ON DUPLICATE KEY UPDATE nick = VALUES(nick)表示“使用本次 INSERT 中nick的值”,语义清晰且高效; -
移除旧函数:
sql_check_row()、sql_update_row()等自定义函数存在设计缺陷,应弃用,直接使用原生mysqli预处理。
? 总结
重复数据不是逻辑错误,而是并发场景下缺乏原子性保障的必然结果。永远不要依赖“应用层判断 + 分步执行”的方式处理唯一键冲突。INSERT ... ON DUPLICATE KEY UPDATE(或 REPLACE INTO)是 MySQL 下最简洁、最安全、最高性能的标准方案。同时,请立即为 unique_key 添加数据库级唯一约束——这是数据一致性的最后一道防线。











