"小王,咱们的优惠券发放系统又被刷爆了!"凌晨两点,运维同事的电话把刚入睡的小王惊醒,打开监控一看,系统正在经历一波恶意刷券攻击——同一个用户ID在短短1秒内发起了上百次领取请求,而系统竟然全都处理了。
这不是小王第一次遇到这种问题,在电商大促、秒杀活动等高并发场景下,重复请求就像野草一样疯长,如何在海量请求中快速识别并过滤重复数据?今天我们就来聊聊用Redis这把"瑞士军刀"解决数据去重的实战方案。
在传统方案中,我们可能会选择数据库唯一索引或者应用层缓存来判断数据是否重复,但在高并发场景下,这些方案都存在明显短板:
Redis之所以成为去重利器,核心在于三点:
import redis r = redis.Redis(host='localhost', port=6379) def check_duplicate(user_id, coupon_id): key = f"coupon:{coupon_id}:users" # 如果用户已存在集合中,返回True表示重复 return r.sismember(key, user_id) def add_user(user_id, coupon_id): key = f"coupon:{coupon_id}:users" r.sadd(key, user_id) # 设置24小时过期 r.expire(key, 86400)
适用场景:中小规模去重(单个集合建议不超过1万元素) 优点:实现简单,内存占用较小 注意点:大数据量时考虑分片,比如按用户ID哈希分桶
当需要处理亿级数据去重时,布隆过滤器是更经济的选择:
from redisbloom.client import Client rb = Client() # 初始化过滤器(预计处理1亿条数据,误判率0.1%) rb.bfCreate('coupon_users', 0.001, 100000000) def check_duplicate(user_id): return rb.bfExists('coupon_users', user_id)
适用场景:允许极小概率误判的超大规模去重 优点:空间效率极高,1亿数据仅需约114MB内存 注意点:存在误判可能,且不支持删除操作
def check_duplicate(request_id): key = f"req:{request_id}" # setnx原子操作:key不存在时设置并返回1,已存在返回0 return not r.setnx(key, 1) def set_expire(request_id, ttl=60): r.expire(f"req:{request_id}", ttl)
适用场景:短时效的请求去重(如60秒内防重) 优点:极其轻量,适合高频短生命周期场景 注意点:需要合理设置TTL避免内存堆积
当需要统计不重复元素数量时:
def count_unique_visitors(page_id, user_id): key = f"page:{page_id}:uv" r.pfadd(key, user_id) def get_uv_count(page_id): return r.pfcount(f"page:{page_id}:uv")
适用场景:只需要计数不关心具体内容的场景 优点:12KB内存可统计2^64个不重复元素 注意点:有约0.81%的标准误差
def safe_check_duplicate(request_id): lock_key = f"lock:{request_id}" try: # 获取分布式锁(10秒超时) if r.set(lock_key, 1, nx=True, ex=10): return _real_check_duplicate(request_id) else: time.sleep(0.1) return safe_check_duplicate(request_id) finally: r.delete(lock_key)
-- 检查并设置的原子操作 local key = KEYS[1] local value = ARGV[1] local ttl = tonumber(ARGV[2]) local exists = redis.call("EXISTS", key) if exists == 0 then redis.call("SET", key, value) redis.call("EXPIRE", key, ttl) return 0 else return 1 end
# redis.conf 关键配置 hash-max-ziplist-entries 512 set-max-intset-entries 512 activerehashing yes
方案 | 10万QPS延迟 | 内存占用(100万数据) | 准确性 |
---|---|---|---|
Set | 3ms | ~80MB | 100% |
Bloom Filter | 5ms | ~1.2MB | 9% |
String | 2ms | ~60MB | 100% |
HyperLogLog | 4ms | ~12KB | ≈99.2% |
选择哪种Redis去重方案,就像选择不同的筛子——Set是精密的金属滤网,Bloom Filter是高效的大孔渔网,HyperLogLog则是聪明的统计学家,根据你的业务场景(数据规模、准确性要求、时效需求)选择合适的工具,才能在高并发洪流中游刃有余。
没有最好的方案,只有最适合的方案,下次当你面对海量重复数据时,不妨先问问自己:这次该用Redis的哪种"筛法"?
本文由 童秋颖 于2025-08-04发表在【云服务器提供商】,文中图片由(童秋颖)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/532357.html
发表评论