布隆过滤器

布隆过滤器是一种概率型数据结构,用于检查集合中是否存在某个项目

布隆过滤器是 Redis 开源版中的一种概率型数据结构,它使用固定大小的极小内存空间来检查集合中是否存在某个元素。

布隆过滤器不存储集合中的所有项目,而是仅存储项目的哈希表示,因此会牺牲一定的精度。这种权衡的优势在于布隆过滤器非常节省空间且速度很快。

布隆过滤器可以保证某个项目不存在于集合中,但只能对其存在性进行估计。因此,当它响应某个项目不存在于集合中(否定回答)时,你可以确信事实确实如此。但每 N 个肯定回答中会有一个是错误的。尽管乍一看这似乎很不寻常,但这种不确定性在计算机科学中仍然有其应用之地。在许多情况下,否定回答可以防止更昂贵的操作,例如检查用户名是否已被占用、信用卡是否已被报告为被盗、用户是否已看过广告等等。

用例

金融欺诈检测(金融)

此应用回答了“用户之前是否在此位置支付过?”的问题,从而检查用户购物习惯中的可疑活动。

每个用户使用一个布隆过滤器,并在每笔交易时进行检查。提供极快的响应(本地延迟)。如果用户移动,则在不同区域进行复制。防止性能随规模增长而下降。

为此类应用使用 Redis 布隆过滤器可带来以下好处

  • 快速完成交易
  • 降低网络分区导致交易中断的可能性(连接需要保持打开的时间更短)
  • 为信用卡持卡人和零售商提供额外的安全层

布隆过滤器在金融行业还可以帮助回答的其他问题包括

  • 用户是否曾在此类产品/服务中进行过购买?
  • 当用户在经过验证的在线商店(如亚马逊、苹果应用商店等大型零售商)购物时,我是否需要跳过一些安全步骤?
  • 这张信用卡是否已被报告为遗失/被盗?在后一种情况下使用布隆过滤器还有一个额外的好处是,金融机构可以交换其被盗/被封锁的信用卡号列表,而无需透露号码本身。

广告投放(零售、广告)

此应用回答了以下问题

  • 用户是否已看过此广告?
  • 用户是否已购买此产品?

为每个用户使用一个布隆过滤器,存储所有已购买的产品。推荐引擎建议新产品,并检查该产品是否存在于用户的布隆过滤器中。

  • 如果不存在,则向用户展示广告并将其添加到布隆过滤器中。
  • 如果存在,则重新开始该过程并重复,直到找到一个不存在于过滤器中的产品。

为此类应用使用 Redis 布隆过滤器可带来以下好处

  • 实现定制化近实时体验的经济高效方式
  • 无需投资昂贵的基础设施

检查用户名是否被占用(SaaS,内容发布平台)

此应用回答了以下问题:此用户名/电子邮件/域名/slug 是否已被使用?

为每个已注册的用户名使用一个布隆过滤器。新用户输入所需的用户名。应用检查用户名是否存在于布隆过滤器中。

  • 如果不存在,则创建用户并将用户名添加到布隆过滤器中。
  • 如果存在,应用可以决定是检查主数据库还是拒绝该用户名。

查询时间随规模增长保持不变。

为此类应用使用 Redis 布隆过滤器可带来以下好处

  • 执行常见操作的非常快速高效的方式
  • 无需投资昂贵的基础设施

示例

考虑一家生产一百万种不同自行车的自行车制造商,您想避免在新车型中使用重复的型号名称。布隆过滤器可用于检测重复项。在下面的示例中,您将创建一个可容纳一百万条记录、错误率为 0.1% 的过滤器。添加一个型号名称并检查其是否存在。然后添加多个型号名称并检查其是否存在。

注意:即使只有少数项目,也始终存在误报的可能性,这意味着即使某个项目未明确添加到布隆过滤器中,它也可能“存在”。要更深入地理解布隆过滤器的概率性质,请查阅页面底部链接的博客文章。

预留布隆过滤器

使用 Redis 布隆过滤器,大部分大小计算工作都已为您完成

BF.RESERVE {key} {error_rate} {capacity} [EXPANSION expansion] [NONSCALING]

1. 误报率 (error_rate)

该比率是介于 0 和 1 之间的十进制值。例如,对于期望的 0.1% 误报率(千分之一),error_rate 应设置为 0.001。

2. 预期容量 (capacity)

这是您预期过滤器中总共包含的项目数量,对于静态集合来说很简单,但当集合随时间增长时,这会变得更具挑战性。正确确定这个数字很重要,因为如果**容量过大**,最终会浪费内存。如果**容量过小**,过滤器将会满载,并且必须在其顶部叠加新的子过滤器(子过滤器叠加)。当过滤器由多个子过滤器叠加而成时,添加操作的延迟保持不变,但存在性检查的延迟会增加。这是由于检查的工作方式:常规检查会首先在顶部(最新的)过滤器上执行,如果返回否定回答,则检查下一个,依此类推。延迟增加正是源于此。

3. 扩展 (EXPANSION)

向布隆过滤器添加项目不会因为数据结构“填满”而失败。相反,误报率会开始增长。为了使错误率接近过滤器初始化时设置的值,布隆过滤器会自动扩展,即当达到容量时,将创建一个额外的子过滤器。
新子过滤器的大小是最后一个子过滤器的大小乘以 EXPANSION。如果过滤器中要存储的项目数量未知,我们建议您使用 2 或更大的扩展因子,以减少子过滤器的数量。否则,我们建议您使用扩展因子 1,以减少内存消耗。默认扩展因子为 2。

过滤器将为每个新的子过滤器继续添加更多哈希函数,以保持您期望的错误率。

您可能会想“既然我知道会扩展,为什么还要创建容量较小、扩展率较高的过滤器?”答案是:对于需要保留大量过滤器(例如每个用户或每个产品一个过滤器)的情况,其中大多数过滤器将保持较小,但一些活动较多的过滤器将不得不扩展。

4. NONSCALING

如果您知道不会扩展,请使用 NONSCALING 标志,因为这样过滤器将少使用一个哈希函数。请记住,如果您确实达到了最初分配的容量,您的错误率将开始增长。

布隆过滤器的总大小

布隆过滤器实际使用的内存是所选错误率的函数

最优哈希函数数量为 ceil(-ln(error_rate) / ln(2))

给定所需的 error_rate 和最优哈希函数数量,每个项目所需的比特数为 -ln(error_rate) / ln(2)^2。因此,过滤器所需的总比特数为 capacity * -ln(error_rate) / ln(2)^2

  • 1% 错误率需要 7 个哈希函数和每个项目 9.585 比特。
  • 0.1% 错误率需要 10 个哈希函数和每个项目 14.378 比特。
  • 0.01% 错误率需要 14 个哈希函数和每个项目 19.170 比特。

作为比较,当使用 Redis 集合进行成员测试时,所需的内存是

memory_with_sets = capacity*(192b + value)

例如,对于一组 IP 地址,每个项目大约需要 40 字节(320 比特),这比我们使用误报率为 0.01% 的布隆过滤器所需的 19.170 比特要高得多。

布隆过滤器 vs Cuckoo 过滤器

布隆过滤器在插入项目时通常表现出更好的性能和可扩展性(因此,如果您经常向数据集添加项目,那么布隆过滤器可能是理想选择)。Cuckoo 过滤器在检查操作上更快,并且允许删除。

性能

布隆过滤器的插入操作复杂度为 O(K),其中 k 是哈希函数的数量。

检查项目操作复杂度为 O(K),对于叠加过滤器为 O(K*n),其中 n 是叠加过滤器的数量。

学术资源

参考资料

网络研讨会

  1. 概率型数据结构 - Redis 中你可能没有使用的最有用的东西

博客文章

  1. RedisBloom 快速入门教程
  2. 使用布隆过滤器进行开发
  3. Redis Enterprise 上的 RedisBloom
  4. 可能是,也可能不是:Redis、RedisBloom 和布隆过滤器
  5. RedisBloom – 用于 Redis 的布隆过滤器数据类型
评价本页
返回顶部 ↑