结论:使用Redis的String类型存储验证码,key设计为"captcha:phone:{phone}:sms:{expireTime}",设置TTL过期时间为5分钟,实现自动清理;生成验证码时用pipeline批量操作减少网络往返,提升性能;验证时原子性检查验证码和过期时间,使用Lua脚本确保线程安全;高效方案:结合Hash存储多字段或List临时队列,针对高并发场景用Redis Cluster分片,显著提升用户体验,减少数据库压力。
来源1
在用户注册或登录时,我们需要发送验证码到用户的手机上。为了防止恶意用户频繁请求验证码,我们需要对每个手机号码设置发送频率限制,比如每个手机号码每分钟最多发送3次验证码,每小时最多发送10次验证码。为了实现这个功能,我们可以使用Redis的String类型来存储每个手机号码的发送次数和最后发送时间。
来源2
key设计:为了避免key冲突,我们可以将手机号码作为key的一部分,比如:sms:code:phone:138xxxxxxxx。验证码有效期一般为5分钟,所以我们设置key的过期时间为300秒。生成验证码:使用随机数生成6位验证码,比如123456。存储到Redis:使用SETEX命令原子性地设置验证码和过期时间SETEX sms:code:phone:138xxxxxxxx 300 123456。
来源3
验证逻辑:获取验证码:使用GET命令获取存储的验证码GET sms:code:phone:138xxxxxxxx。比对验证码:如果用户输入的验证码与Redis中存储的验证码一致,则验证成功,并删除该keyDEL sms:code:phone:138xxxxxxxx。防止重复使用:删除key确保验证码只能使用一次。错误处理:验证码不存在或已过期返回错误,验证码不匹配返回错误。
来源4
高效存储方案:使用Pipeline批量操作:发送验证码时,可能需要更新多个计数器(如分钟计数、小时计数),使用pipeline可以减少RTT。多级限流:分钟级key: rate:sms:phone:138xxxxxxxx:minute:{minute},小时级key: rate:sms:phone:138xxxxxxxx:hour:{hour}。Lua脚本原子验证:为了防止竞态条件,使用Lua脚本原子性地验证验证码并删除key。
来源5
Redis lua脚本示例:local code = redis.call('get', KEYS[1]) if code == ARGV[1] then redis.call('del', KEYS[1]) return 1 else return 0 end 调用:EVAL script 1 sms:code:phone:138xxxxxxxx 123456。监控和告警:使用INFO命令监控Redis内存使用,设置maxmemory-policy allkeys-lru。数据持久化:开启AOF持久化,确保数据安全。
来源6
高并发优化:使用Redis Sentinel高可用,主从复制读写分离。对于海量用户,使用Redis Cluster分片。验证码生成优化:使用雪花算法或redis.incr生成唯一ID,避免重复。缓存穿透防护:验证码不存在时不缓存空值,使用布隆过滤器辅助判断。结合验证码与session:验证成功后生成session token,一次性token机制提升安全。
来源7
实际代码实现(Java):@Autowired private RedisTemplate
FAQ
Q: 验证码过期后Redis key怎么自动清理?
A: 使用SETEX或EXPIRE设置TTL,Redis自动删除过期key。
Q: 如何防止短信轰炸攻击?
A: 多级限流,如每分钟3次、每小时10次,用INCR和EXPIRE实现计数器。
Q: 高并发下验证验证码有竞态条件吗?
A: 用Lua脚本原子执行GET+比对+DEL操作。
Q: Redis内存满了怎么处理?
A: 设置maxmemory和eviction policy如allkeys-lru,监控使用INFO命令。