用Redis来快点搞定数据排重,省时又高效的那些事儿
- 问答
- 2026-01-13 08:35:49
- 1
综合自多位开发者的实践经验分享及Redis官方文档的常见用例)
用Redis来快点搞定数据排重,省时又高效的那些事儿
咱们做开发的,或者搞数据的,经常会遇到一个挺烦人的问题:怎么快速判断一个东西是不是已经存在过?爬虫要判断这个网页链接有没有抓取过,防止白费力气;又比如,用户注册时,要检查用户名或者手机号有没有被别人抢先用了;再比如,统计一天的独立访问用户数,同一个用户来再多次也只能算一个。
这些事儿,说白了就是“数据排重”,要是数据量小,随便你怎么玩都行,扔到数据库里查一下呗,但一旦数据量上了规模,比如几百万、几千万甚至更多,再用数据库去SELECT COUNT(*),那速度可就慢得让人想睡觉了,数据库的压力也山大。
这时候,Redis就该闪亮登场了,它是个内存数据库,数据都放在内存里,读写速度飞快,简直就是为这种需要快速判断的场景量身定做的,用它来排重,那叫一个爽快。
Redis里排重的“三板斧”
Redis有好几种数据结构,用来做排重,最常见、最顺手的主要是这三样:Set(集合)、Bitmap(位图),还有Bloom Filter(布隆过滤器),它们各有各的用武之地,咱们一个一个说。
第一板斧:Set集合——简单直接的小能手
Set是Redis里最简单的一种集合,它的特点就是里面的元素都是唯一的,自动帮你去重,你往里面塞东西,如果已经存在了,它就默默地忽略掉。
- 怎么用? 命令超级简单,比如判断用户ID是否已经存在:
SADD user_set 123456(把用户ID 123456塞进集合user_set里,如果之前没有,返回1表示新加的;如果已经有了,返回0,表示没加进去)SISMEMBER user_set 123456(查询用户ID 123456在不在集合里,在就返回1,不在返回0)
- 啥时候用最合适?
- 当你要排重的数据量不是特别特别巨大的时候(比如百万级别以内,具体看你的内存大小)。
- 当你不仅想知道是否存在,还想偶尔看看全部都有哪些不重复的项,或者随机取几个出来的时候,因为Set能很方便地列出所有成员。
- 优点: 思路直观,操作命令简单,除了判断存在性,还能做交集、并集等更复杂的操作。
- 缺点: 如果数据量极大,比如要处理上亿个元素,那么占用的内存会比较大,因为每个元素都会作为一个独立的字符串存储,虽然比数据库快多了,但还不是最省内存的方案。
第二板斧:Bitmap位图——极致节省的计数专家
Bitmap不是一种单独的数据类型,它其实是基于String类型实现的一套位操作,你可以把它想象成一个超长的、由0和1组成的数组,每个位(bit)的下标代表一个元素,比如用户ID,位的值,0表示不存在,1表示存在。
- 怎么用? 比如我们还是判断用户ID是否存在,假设我们的用户ID是数字:
SETBIT user_bitmap 123456 1(把第123456位的值设置为1,注意,这里SETBIT会自动扩展位数组的长度)GETBIT user_bitmap 123456(获取第123456位的值,是1就说明存在)- 还可以用
BITCOUNT user_bitmap来快速统计总共有多少个1,也就是不重复的用户数,这个操作也很快。
- 啥时候用最合适?
- 当你要排重的元素是连续的整数,或者可以映射成连续整数(比如数据库的自增ID)的时候,这是Bitmap的绝对主场。
- 当数据量巨大,并且对内存极度敏感的时候,因为一个Bit只能表示一个元素是否存在,所以它非常非常省内存,理论上,用Bitmap存储1亿个元素的排重信息,只需要大约12MB内存(100,000,000 / 8 / 1024 / 1024 ≈ 11.92MB),而Set可能需要几百MB甚至更多。
- 非常适合做那种“用户是否签到”、“某天是否登录”这类二值状态的统计。
- 优点: 内存消耗极低,对于大数据量的01状态存储,性价比无敌,位操作速度也极快。
- 缺点: 如果元素ID不是连续的整数,或者非常稀疏(比如只有第1位和第1亿位是1,中间大部分是0),那Bitmap可能还是会浪费一些空间(因为它会分配从0到最大ID的连续空间),而且它只能回答“是否存在”,不能告诉你具体有哪些元素。
第三板斧:Bloom Filter布隆过滤器——面对海量的“可能”大师
Bloom Filter这个名字听起来有点高级,但其实思想很巧妙,它是一种概率型数据结构,特点是:它告诉你某个元素“一定不存在”或者“可能存在”,注意,是“可能存在”,有微小的误判概率。
- 为啥要这样? 就是为了在海量数据排重时,用极小的空间代价换来极高的速度,它底层也是用了类似Bitmap的位数组,但一个元素会通过多个不同的哈希函数,映射到位数组上的多个点,判断存在性时,只有所有这些点都是1,才说“可能存在”。
- 怎么用? 在Redis 4.0之后,官方提供了模块支持,可以用
BF.ADD和BF.EXISTS这样的命令来直接操作布隆过滤器,用法和Set很像:BF.ADD my_filter "example_url"(向布隆过滤器my_filter添加一个URL)BF.EXISTS my_filter "example_url"(检查URL是否存在,返回1表示“可能存在”,返回0表示“一定不存在”)
- 啥时候用最合适?
- 当你的数据量超级巨大,用Set和Bitmap都觉得吃力或者不合适的时候。
- 当你的业务可以容忍极低概率的误判时,在爬虫中,用一个布隆过滤器来判断URL是否已抓取,即使有万分之一的概率误判(把一个新URL当成抓取过的),顶多是漏掉极少数网页,但能换来内存的极大节省和速度的极致提升,这是完全可以接受的,但如果用在金融交易ID排重,要求100%准确,那就不行。
- 优点: 空间效率和查询时间都远远超过一般的算法,在海量数据排重场景下优势巨大。
- 缺点: 有一定的误判率,并且不支持删除元素(传统的布隆过滤器是这样,现在有变种可以支持,但更复杂)。
总结一下
用Redis搞排重,就是这么回事儿:
- 数据量不大,求稳求简单:用Set,准没错,功能还多点。
- 数据量很大,元素是数字ID,追求极致内存节省:用Bitmap,省内存省到极致。
- 数据量海海漫漫,元素可能是字符串,且能接受一点点不确定性:用Bloom Filter,它是应对这种场景的神器。
说白了,Redis提供的这些工具,就是让我们不用再去傻傻地循环查数据库,把O(n)的复杂度瞬间降到接近O(1),速度提升成百上千倍都不是梦,关键是,理解了每种工具的脾气秉性,用在合适的场景里,就能真正体会到什么叫“省时又高效”。

本文由黎家于2026-01-13发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/79825.html
