一、Bitmap和布隆过滤器区别
有一道面试题没回答上来:Redis 的Bitmap和布隆过滤器啥区别与关系?
Redis中的Bitmap(位图)是一种较为特殊数据类型,它以最小单位bit来存储数据,我们知道一个字节由 8个 bit 组成,和传统数据结构用字节存储相比,这使得它在处理大量二值状态(true、false 或 0、1等只有两种状态)数据时具有极高的空间效率。不过,它不是一种全新的数据类型,其底层实现仍是基于 String 类型。
Bitmap特性:
优点
- 极高空间效率:bitmap 是真的节省数据存储空间。粗略的算一下,一亿位的 Bitmap 大概才占 12MB 的内存,相比其他数据结构,能极大地节省存储空间;
- 快速查询:位操作通常比其他数据结构查询速度更快。无论是设置位值还是获取位值,时间复杂度都为 O (1),能够快速响应查询请求;
- 易于操作:支持单个位操作、位统计、位逻辑运算等,运算效率高,不需要进行比较和移位;
缺点
- 由于数据结构特点,导致它仅适用于表示两种状态,即 0 和 1。对于需要表示更多状态的情况,Bitmap 就不适用了;
- 只有当数据比较密集时才有优势,如果我们只设置(20,30,888888888)三个偏移量的位值,则需要创建一个 99999999 长度的 BitMap ,但是实际上只存了3个数据,这时候就有很大的空间浪费,碰到这种问题的话,可以通过引入另一个 Roaring BitMap 来解决;
应用场景
看到 Bitmap 还是比较简单的一种数据结构,占用空间小查询效率高,非常适用于记录状态的场景,它的应用场景很常见,比如:
- 用户签到状态(连续签到天数)
- 用户的在线状态(统计活跃用户)
- 问卷答题等等吧!
布隆过滤器特性:
优点
- 布隆过滤器的空间占用也是极小,它本身不存储完整的数据,和 bitmap 一样底层也是通过 bit 位来表示数据是否存在。
- 性能比较稳定,无论集合中元素的数量有多少,插入和查询操作的时间复杂度都非常低,通常为 O (k),其中 k 是哈希函数的个数。也就是说在处理大规模数据时,布隆过滤器的性能不会随着数据量的增加而急剧下降。
缺点
- 存在一定的误识别率:布隆过滤器存在误判的情况,即当一个元素实际上不在集合中时,有可能被判断为在集合中。这是因为多个元素可能通过哈希函数映射到相同的位置,导致误判。但是,当布隆过滤器判断一个元素不在集合中时,则是 100% 正确的。
- 删除元素比较困难:一般情况下,不能直接从布隆过滤器中删除元素。这是因为一个位置可能被多个元素映射到,如果直接将该位置的值置为 0,可能会影响其他元素的判断。
应用场景
- 布隆过滤器存在一定的误判,所以使用它的场景就一定要允许不准确的情况发生:
- 解决 Redis 缓存穿透问题:秒杀商品详情通常会被缓存到 Redis 中。如果有大量恶意请求查询不存在的商品,通过布隆过滤器可以快速判断这些商品不存在,从而避免了对数据库的查询,减轻了数据库的压力。
- 邮箱黑名单过滤:在邮件系统中,可以使用布隆过滤器来过滤垃圾邮件和恶意邮件。将已知的垃圾邮件发送者的地址或特征存储在布隆过滤器中,新邮件来时判断发送者是否在黑名单中。
- 对爬虫网址进行过滤:在爬虫程序中,为了避免重复抓取相同的网址,可以使用布隆过滤器来记录已经抓取过的网址。新网址出现时,先判断是否已抓取过。
Bitmap 和布隆过滤器的实现虽然都基于位数组,但它们的使用场景和特性有显著不同。Bitmap 适用于准确的集合查询,布隆过滤器则适用于大规模数据且对误判不敏感的场景。
布隆过滤器通过把字符串经过多个Hash函数,映射到bitmap上。布隆过滤器优化了hash函数,进入多个函数,且支持调整误判因子。
二、布隆过滤器和HyperLogLog异同
HyperLogLog(HLL)和Bloom Filter(布隆过滤器)是两种常用于大规模数据处理的概率数据结构,它们用于解决不同的问题,并具有不同的特性和应用场景。
相同之处:
1,两者都是概率型数据结构,用于在内存中高效地处理大量数据。
2,它们都可以用于判断某个元素是否存在于一个集合中,但对存在与否的判断有一定的错误率。
3,删除操作:HLL不支持删除已经添加的元素,只能通过创建一个新的HLL数据结构来实现删除操作。
布隆过滤器无法直接删除元素,因为删除一个元素可能会影响到其他元素的判断结果。
区别之处:
布隆过滤器和HyperLogLog都是用于基数统计的数据结构,但它们在实现方式、内存占用和误差率等方面有所不同。
1,数据处理问题的不同:HLL主要用于基数估计(即估计一个集合中不同元素的个数),
而布隆过滤器用于判断一个元素是否存在于一个集合中。
2,内存使用:HLL使用的内存空间相对较少,通常只需要几千字节。
布隆过滤器需要的内存空间相对较多,它的空间需求与预期的误判率和待存储元素数量有关。
3,精确性:HLL提供的基数估计结果是近似值,通常具有较小的相对标准误差。
布隆过滤器在元素存在判断上具有绝对的精确性,但存在一定的误判率。
作用的不同:
- HLL的主要作用是对大数据集合的基数进行估计。它可以用于统计网站的独立访客数、社交网络中的活跃用户数等。
- 布隆过滤器的主要作用是快速判断一个元素是否存在于一个集合中,通常用于缓存失效判断、防止重复提交、快速过滤不符合条件的数据等场景。
总之,HyperLogLog和Bloom Filter是两种不同的概率数据结构,分别适用于基数估计和快速元素判断的场景。选择使用哪种算法取决于具体的需求和应用场景。
三、知识补充
3.1 HyperLogLog
- 作用:类似于一个set集合,将元素存入后,可以统计集合中不重复的元素个数(基数统计)。
- 原理:基于数学和概率,较为复杂
- 特点:元素统计的无插入为0.81%,元素的数量非常庞大时占用的空间却总是很小
适用场合:
- 适合于海量数据的基数统计,
- 如百万级用户的UV统计
使用:
- PFADD key element [element ...] # 添加指定元素到 HyperLogLog 中
- PFCOUNT key [key ...] # 返回给定 HyperLogLog 的基数估算值。
- PFMERGE destkey sourcekey [sourcekey ...] # 将多个 HyperLogLog 合并为一个 HyperLogLog
3.2 布隆过滤器
- 作用:类似于一个set集合,将元素存入后,可以判定元素在集合中不存在或可能存在两种情况。
- 原理:很长的二进制向量和多组无偏哈希映射函数(对于一个key的多组哈希函数的哈希值分布较均匀)对于一个元素key,对多个哈希函数的哈希值取模后在二进制位数组中置位。在查询时,如果这些位全为1则元素存在(可能误判,因为哈希冲突),如果有0则一定不存在。
特点:
- 不存储元素本身,保密性强
- 主要用于判断其是否存在,且非常节省空间
- 添加数据和判断是否存在的查询操作速度很快,O(n) n为哈希函数的个数,较小
- 能判定元素不存在但不能保证元素一定存在(因为可能哈希冲突)
- 很难对已加入的数据进行删除。
降低误判率的方法
- 增大位数组,用更多的位来表示元素,哈希冲突概率降低,但相应的空间占用空间会变大
- 增加无偏哈希函数的数量,哈希冲突概率降低,但相应的计算的耗时会增加
适用场合
- 解决Redis的缓存穿透问题
- 黑名单过滤
3.3 BitMap
-
作用:bit数组,用很少的空间存储大量的二分状态(0/1),并用偏移来定位数据、统计置位的数量和状态。
-
原理:底层是Redis的String类型。
-
特点:只保存0或1的值,极致节省。
适用场合:
-
签到
-
用户是否在线
总结:
- Hypeloglog 使用少量固定空间以及基数统计算法,适用于大数据情况下能接收微小出错的统计场景。
- Bitmap 使用sds实现的位数组,sds逆序存储位数组扩容时不用修改旧数据,适用于大数据情况下只有两个状态的统计场景。
- Bloom Filter 使用位数组与多个哈希函数实现,适用于在大数据情况下且能接收微小出错的判断元素是否存在集合的场景。