
去重别再用内存 Set 了3 种方案对比与选型任务做去重大多数人的第一反应是seen set()处理前if x in seen: continue。小批量没问题可一旦任务量上来这个 set 会成为最容易被忽视的坑。这篇把三种主流去重方案摆出来对比内存 Set、布隆过滤器、Redis。一、内存 Set 到底哪里不行seenset()fortaskintasks:iftaskinseen:continueseen.add(task)handle(task)三个问题吃内存。一个字符串存进 set加上 Python 对象的开销一条动辄上百字节。100 万条就是上百 MB1000 万条直接把内存压垮。重启即失忆。进程一挂set 就没了断点续跑时前面的全重来。多进程不共享。开了几个进程每个进程一份 set互相不知道对方做没做过。所以set只适合小批量、单进程、不需要断点续跑的场景大致 10 万以内。二、布隆过滤器极省内存的概率去重布隆过滤器的思路是用很少的位数记住哪些一定没来过。它把每个元素用几个哈希函数映射到位数组的几位上。查询时如果有一位是 0就一定没来过如果全是 1则可能来过。frompybloom_liveimportBloomFilter bfBloomFilter(capacity1_000_000,error_rate0.001)fortaskintasks:iftaskinbf:continuebf.add(task)handle(task)特点很鲜明省内存100 万条、误判率 0.1%也就几 MB有误判会把少数没做过的任务判成做过了——注意方向它不会漏判做过的不会说没做过不支持删除删不掉单个元素。适合海量、允许极小比例漏处理的场景。三、Redis能持久化、能共享要断点续跑、要多个进程一起用就得把去重状态放到进程外面。Redis 是最常用的选择。importredis rredis.Redis()defhandle_once(task):keydone:str(hash(task))# setnx 返回 True 表示之前没设置过即第一次处理ifnotr.set(key,1,nxTrue,ex7*86400):return# 已经处理过do_work(task)要点SET key val NX是原子操作天然适合做第一次才处理的判重加EX过期时间避免 key 无限增长想省内存、又接受误判可以用 Redis 的布隆过滤器模块BF.ADD / BF.EXISTS。代价是要多维护一个 Redis 实例——小项目可能觉得重但需要断点续跑时这个代价值得。四、三种方案对比方案内存准确性持久化跨进程适合规模内存 Set高100% 准否否10 万内布隆过滤器极低有误判可选看实现百万级以上Redis中100% 准是是需断点续跑五、到底怎么选一句话看规模和是否需要续跑。任务不到 10 万、单进程跑完就完 →内存 Set别过度设计百万级、只关心做过没有、能接受极低漏判 →布隆过滤器要断点续跑、要多进程 / 多机共享 →Redis既要省内存又要精确 →Redis 布隆模块用布隆挡掉绝大多数少量不确定的再查精确集合。还有个容易被忽略的点去重用的键要选对。用任务的全部参数做 hash而不是只用某一个字段——参数不同其实是两个不同的任务。六、小结你的场景选它小批量、单进程内存 Set海量、可接受误判布隆过滤器要断点续跑 / 多进程共享Redis又省内存又要精确Redis 布隆去重不是加个 set 就完事选错方案的代价要么是内存爆要么是重启后从头再来。互动时间你做批量任务去重时用的哪种方案有没有被内存或误判坑过评论区聊聊 声明本文为原创技术分享涉及的采集思路仅供学习与合法用途参考。请遵守目标网站的 robots 协议及相关法律法规勿将技术用于任何违规场景。