如何控制毫秒/微秒级并发场景下的重复API调用
并发重复提交问题解决方案
针对你遇到的POST API并发重复插入问题,结合不能加唯一约束、校验逻辑无法全量放入事务的限制,给你几个可落地的方案:
1. 分布式锁+幂等键拦截
- 实现逻辑:
- 针对每个请求生成唯一幂等键:用客户唯一标识(如ID)+ 请求核心业务参数(如客户手机号、提交的业务单号)做哈希(比如MD5/SHA256),作为该请求的唯一标识。
- 用Redis这类分布式缓存实现锁机制:请求到达时,先尝试用
SETNX(或Redis Redlock)获取以幂等键为Key的锁,锁的过期时间设置为业务处理的最长耗时(比如5秒)。 - 拿到锁的请求进入正常校验+插入流程;未拿到锁的请求直接返回"重复提交"提示,或等待锁释放后再次校验(根据业务需求选择)。
- 注意点:
- 锁的过期时间必须大于业务最长处理时间,避免锁提前释放导致重复;如果处理超时,可考虑续锁机制。
- 封装通用拦截器/注解,让上百个API快速接入,不用逐个修改业务代码。
2. 缓存标记前置拦截
- 实现逻辑:
- 首次请求通过校验后,在缓存中写入一个标记(Key为客户标识+业务类型,Value为"PROCESSING"),过期时间设为业务处理最长耗时。
- 后续相同客户的相同业务请求到达时,先查询缓存:如果存在标记,直接拦截;如果不存在,再进入校验流程。
- 业务处理完成(无论成功失败),删除缓存标记;如果服务异常崩溃,依赖缓存过期自动清理标记,避免永久拦截。
- 优势:比分布式锁更轻量,无需等待锁释放,直接快速拦截重复请求。
3. 数据库行级锁实现伪排他校验
- 实现逻辑:
- 在核心业务表(比如客户主表)新增一个
processing_flag字段(类型为VARCHAR或BOOLEAN,默认NULL)。 - 请求到达后,先执行
UPDATE customer SET processing_flag = 'LOCKED' WHERE customer_id = ? AND processing_flag IS NULL,判断影响行数:- 影响行数>0:说明拿到了排他权限,进入校验+插入流程;
- 影响行数=0:说明已有请求在处理,直接返回重复提交。
- 事务提交/回滚后,
processing_flag会自动恢复为NULL(因为UPDATE在事务内执行),无需手动清理。
- 在核心业务表(比如客户主表)新增一个
- 注意点:无需引入额外组件,利用数据库自身的行锁机制,适合不想增加缓存依赖的场景。
4. 请求排队+消费端去重
- 实现逻辑:
- 在API入口层(网关/服务入口)将请求发送到消息队列(如Kafka/RabbitMQ),用客户标识+请求哈希作为消息的Key。
- 消息队列的同一Key消息会被分配到同一个分区,消费端串行处理该分区的消息。
- 消费端处理前,先查询本地缓存/数据库是否已处理过该请求(用幂等键判断),如果已处理则直接跳过,否则执行校验+插入。
- 优势:适合高并发场景,能削峰填谷,同时天然保证同一客户的请求串行处理,避免并发冲突。
内容的提问来源于stack exchange,提问作者Vman
相关产品推荐
相关产品推荐

