位图算法、布隆过滤器简介

412 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

位图算法

位图(bitmap)是二进制的 byte 数组,也可简单理解为一个普通字符串。

核心思想是用 bit 数组来记录 0-1 两种状态,然后再将具体数据映射到这个比特数组的具体位置,这个比特位设置为 0 表示数据不存在,设置成 1 表示数据存在。

bitmap 在大量数据查询、去重等应用场景中使用的比较多,该算法具有较高的空间利用率。

通过 Hash 算法将数据比如 URL 哈希成某个值,然后将位图中的这个位置置为 1。

位图算法还存在一些限制,比如:

  1. 数据碰撞:比如将字符串映射到 bitmap 的时候会有数据碰撞的问题,那么我们可以使用布隆过滤器来解决,布隆过滤器使用多个 Hash 函数过滤来减少冲突的概率。
  2. 数据稀疏:又比如要存入(10,8887983,93452134)这三个数据,我们需要建立一个 9999999 长度的 bitmap,但是实际上只存了 3 个数据,这个时候就有很大的空间浪费。碰到这种问题的话,可以采用 Roaring bitmap 来解决。

常用的业务场景如下:

  1. 用户签到
  2. 用户日活、月活
  3. 用户在线统计以及总在线人数统计
  4. 开屏广告、运营弹窗统计
  5. APP 内用户全局弹窗的小红点

注意,在使用 bitmap 时,不要超出 Redis 的使用范围,因为 bitmap 实际上并不是一种真实的数据结构,它的底层实现为 SDS,只不过操作的粒度变成了位,因为 SDS 最大容量为 512M,所以 bitmap 最多可以存 2^32 位,bitmap 占用内存大小大约为:bitmap 长度 / 8 / 1024 / 1024(单位:MB)。

布隆过滤器

布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数,布隆过滤器可以用于检索一个元素是否在一个集合中,它的优点是空间效率和查询时间都比一般的算法要好得多,缺点是有一定的误识别率和删除困难。

布隆过滤器的原理是,当一个元素被加入集合时,通过 k 个散列函数将这个元素映射成一个位数组中的 k 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点中有任意一个点为 0,则被检索元素一定不在,如果都是 1,则被检元素可能存在。这就是布隆过滤器的思想。

布隆过滤器跟 bitmap 的区别就是,布隆过滤器使用了 k 个哈希函数,每个字符和 k 个 bit 对应。从而降低了冲突的概率。

当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那么一定不存在。

布隆过滤器的缺点:

  • 存在误判:可能要查到的元素并没有在容器中,但是 hash 之后得到的 k 个位置上值都是 1。如果布隆过滤器中存储的是黑名单,那么可以通过建立一个白名单来存储可能会误判的元素。(对于爬虫来讲,允许极少量的 url 误判为重复;如果是用于垃圾邮箱的过滤,可以考虑加上一个白名单,专门存储那些被误判的正常邮箱)
  • 删除困难:一个放入容器的元素映射到 bit 数组的 k 个位置上是 1,删除的时候不能简单的直接置为 0,可能会影响其他元素的判断。