数据库唯一索引是最简单可靠的PHP接口幂等性保障手段,通过为客户端传入的幂等键(如Idempotency-Key)建立唯一索引,利用DB原子性拦截重复插入并抛出23000错误,PHP捕获后查询原记录返回,确保多次请求效果等同一次。

直接用数据库唯一索引是最简单、最可靠、也是生产环境首选的 PHP 接口幂等性保障手段——前提是请求中存在可提取且业务语义明确的幂等键(如 idempotency_key)。
为什么唯一索引能天然保证幂等
数据库在插入时遇到违反唯一约束会立刻报错(如 MySQL 的 1062 Duplicate entry),这个原子性行为不受 PHP 进程并发、重试、网络超时影响。只要把幂等键(比如客户端传的 idempotency_key)作为唯一索引字段,重复请求就会卡在 DB 层,后续逻辑不执行。
注意:这不是“拦截重复”,而是“让重复必然失败”,再配合 PHP 层捕获异常并返回成功响应(因为业务上已处理过)。
- 必须为幂等键字段单独建唯一索引,不能复用主键或业务主键
- 该字段建议设为
VARCHAR(64)或CHAR(32)(如 UUIDv4),避免长文本索引开销 - MySQL 要用
utf8mb4_bin排序规则,防止大小写或空格隐式相等导致去重失效
PHP 中怎么安全捕获并处理唯一索引冲突
不能靠 SELECT + INSERT 判断(竞态条件),必须依赖 INSERT ... ON DUPLICATE KEY UPDATE 或原生 INSERT + 异常捕获。PDO 是最常用方式:
立即学习“PHP免费学习笔记(深入)”;
try {
$pdo->prepare("INSERT INTO orders (idempotency_key, user_id, amount) VALUES (?, ?, ?)")
->execute([$key, $uid, $amt]);
} catch (PDOException $e) {
if ($e->getCode() === '23000' && strpos($e->getMessage(), 'Duplicate entry') !== false) {
// 幂等键已存在 → 查询原始结果并返回
$stmt = $pdo->prepare("SELECT * FROM orders WHERE idempotency_key = ?");
$stmt->execute([$key]);
return $stmt->fetch();
}
throw $e;
}- MySQL 错误码
23000是 SQLSTATE 通用约束违例码,比检查错误信息字符串更稳(但需确认驱动版本支持) - 不要在 catch 里做复杂逻辑(如发消息、调第三方),只查原记录并返回;否则可能引入新幂等问题
- 如果表有自增主键,
INSERT ... ON DUPLICATE KEY UPDATE更高效,但需确保更新字段无副作用(例如不能把created_at改成当前时间)
幂等键从哪来?客户端必须传,PHP 不能生成
幂等键不是 token,也不是时间戳,它必须由调用方在首次请求时生成并重复携带。PHP 层只校验、不生成、不猜测。
- 推荐格式:
idempotency_key: "ord_7f3a9b2c-4d1e-4f8a-ba5f-1234567890ab"(前缀+UUIDv4) - 不能用
md5($_POST)或uniqid()—— 客户端无法复现,重试就变新请求 - HTTP Header 传比 query/body 更规范:
Idempotency-Key: xxx,PHP 用$_SERVER['HTTP_IDEMPOTENCY_KEY']获取(注意下划线转大写) - 如果客户端不传或格式非法,应直接 400 返回,不进入业务流程
真正难的不是加索引或写 try/catch,而是厘清「哪个字段代表一次操作的全局唯一身份」——它得跨服务、跨重试、跨时间有效,且不能随请求参数微小变化而改变。一旦定错幂等键,唯一索引就形同虚设。



















