使用Redis 分布式锁防止短信验证码重复下发问题
发送短信验证码是大多数产品的常见功能,但也常发生“重复下发”问题:用户或客户端多次触发发送接口(误点、重试、网络抖动、并发线程、爬虫/攻击)导致同一手机号短时间内收到多条验证码,从而浪费费用、影响用户体验,甚至触发短信渠道限流。本文聚焦一种简单高效的工程方案:基于 Redis 的分布式锁,并从设计思路、实现要点、性能/复杂度分析、常见坑与解决方案以及测试/监控建议等方面给出实践级指导与代码示例。
一、问题拆解与目标
目标:在保持较好用户体验(及时返回)的同时,避免在短时间内对同一手机号重复下发验证码,并保证系统在分布式部署下的正确性与可恢复性。
常见触发场景:
-
用户多次点击“发送验证码”按钮(误点/网络慢导致重试)。
-
短时间内并发请求(前端并发、并发任务、爬虫)。
-
第三方 SDK 重试或回调重复请求。
设计约束:
-
必须在分布式环境下工作(多台应用实例)。
-
要快速失败或快速返回(不能长时间阻塞客户端)。
-
要有清晰的超时/失败恢复策略,避免死锁或永久“占用”。
二、总体方案(思路)
对同一业务键(通常是 phone
或 phone+bizType
)在 Redis 上加短周期分布式锁。流程:
-
请求到来,计算锁 key(例如
lock:sms:{biz}:{phone}
)。 -
尝试加锁(SET NX + EXPIRE 或 Redisson 等库)。
-
若获取锁失败:返回“处理中/已发送/请稍后”或直接拒绝(依业务决定)。
-
若获取锁成功:在锁保护范围内检查发送频率/黑名单/缓存的最近发送时间;如满足发送条件则调用 SMS 渠道发送,并记录发送时间(缓存或 DB);最后释放锁。
-
保证锁安全释放(只有持有者可以释放),并设计锁 TTL/续租与异常恢复机制(IN_PROGRESS 超时清理等)。
该方案更多是「短粒度串行化」——把同一手机号的并发发送请求序列化,而不是给整系统串行化。
三、Redis 锁的实现要点
1) 简单实现(SET NX + EX)
SET lockKey lockValue NX PX 30000
-
lockKey