简单地说,我们现在是Redis

了解更多

可能和不可能:Redis, RedisBloom和Bloom滤镜

Bloom滤镜可以帮助你的软件变得又轻又快。万博电竞客服了解它们是什么,它们如何工作,以及如何在Redis中使用它们。



回到博客

我遇到麻烦了。我的在线游戏《EverCraft World》(并非真正的游戏)拥有数千万玩家。但如果是的话,那肯定是来自暴风雪海岸!)这是一个很大的问题。这么多的玩家!

但是,当一个新用户注册时,需要花费很长时间来确定他们想要的用户名是否被使用。这是一个足够简单的查询,但庞大的数据量使其速度变慢。

切换到Redis会有帮助。而不是简单但缓慢的SQL查询:

> SELECT COUNT(*) FROM Users WHERE UserName = ' eric'

我们最终得到了一个简单但快速的Redis命令:

>SISSember用户“EricTheCleric”

但所有这些用户仍然占用着堆积如山的内存。数以千万计的用户将这样做。有没有办法使我们的数据更小、更紧凑?

是的,我向你介绍布卢姆过滤器。

布卢姆过滤器到底是什么?

Bloom过滤器是一种概率数据结构谈话关于他们之前.简而言之,概率数据结构使用哈希来为您提供更快、更小的数据结构,以换取一些不确定性。可以将Bloom过滤器视为功能有限的集合。我们只能添加一些东西到它里面,然后检查里面是否有具体的东西。但对这种检查的反应有点奇怪。Bloom过滤器有两种可能的响应:

  1. 那东西肯定不在布景里。
  2. 可能-那东西可能在片场里。

可以这就是奇怪的部分,不确定性。但不要害怕。不确定度很小,完全在您的控制下,使用中的布鲁姆过滤器再开花模块.我将向你们展示如何使用它并控制不确定性。

使用Bloom过滤器与RedisBloom

让我们回到《魔兽世界》。我的游戏拥有数千万独特的用户名,我需要在创造新用户前检查这些用户名。

之前我是这样做的:

>SISSember用户“EricTheCleric”

如果用户名可用,则执行以下操作:

>SADD用户“EricTheCleric”

让我们用Bloom过滤器来看看这个过程。这里有两种可能的情况:

  1. 场景A:用户名已存在。
  2. 场景B:用户名可能已被占用。

场景A:用户名可用

Alice the Allomancer想要创建一个EverCraft的世界账号。我们检查她的用户名是否在Bloom过滤器中:

>BF.EXISTS用户筛选器“AliceTheAllomancer”

男朋友。存在返回0这表明AliceTheAllomancer不在集合中且可用。我们添加她:

>BF.ADD用户筛选器“AliceTheAllomancer”

> SADD用户'AliceTheAllomancer'

容易peasy。

场景B:用户名可能被占用

野蛮人鲍勃也想创建一个帐户。我们看看他的所需用户名位于Bloom筛选器中:

>BF.EXISTS用户筛选器“BobTheBarbarian”

男朋友。存在返回1。用户名可能已被占用。请选择一个新用户名。

现在,我知道你在想什么了。如果真的发生了呢不是拍摄的。难道我们检查吗?如果我们无情地拒绝了Barbarian Bob想要的用户名,而这个用户名实际上是可用的呢?

虽然我能理解不想让一个挥舞着大战斧的野蛮人生气,但这是一个必要的权衡。我们正在以有限的不确定性为代价获得性能和紧凑性。在这种情况下(特别是因为战斧是虚拟的),这是一个很好的交易。

控制的不确定性

如果你打电话BF.ADD并允许它为你创建一个Bloom过滤器,你最终会得到默认值。这些都是很好的小Bloom过滤器的几十项,但对于上面的例子,他们是完全不合适的。

当设置Bloom过滤器时,请使用男朋友。储备控制设置:

>的男朋友。保留UserFilter 0.001 100000000

这将在键下设置Bloom过滤器用户过滤器错误率为0.001或0.1%,可容纳100000000个用户名。

那么Bloom过滤器是如何工作的呢?

布卢姆过滤器的实现非常容易,因为它具有强大的功能。为了准备写这篇博文,我(和其他复述,)写了一个布鲁姆过滤器JavaScript在中使用Node.js和另一个c#请注意,我写这些Bloom过滤器是为了教育,不是为了性能,所以不要在生产中使用它们。

Bloom过滤器本质上是一个指定宽度的位数组。当一个条目被添加到过滤器中时,它将通过指定数量的散列函数(通常是具有不同种子的相同散列算法)运行。这些散列的结果用于生成位数组的索引。对于每个索引,在数组中放入1。

举个例子可能会更容易理解。让我们创建一个8位宽的微小过滤器:

0 0 0 0 0 0 0 0
指数 0 1 2 3. 4 5 6 7

我们使用一个具有两个不同种子的哈希函数。当我们添加一个条目时,我们将调用哈希函数两次以得到两个数字。我们将这些数字通过位宽来生成我们的索引。这是伪代码:

index = some_hash_function('AliceTheAllomancer', seed) % 8

假设异魔者爱丽丝,得到的是2和6。然后通过在位置2和6中放置1来更新位数组。

0 0 1 0 0 0 1 0
指数 0 1 2 3. 4 5 6 7

让我们加入Barbarian Bob。我们通过哈希函数得到一个3和一个6。我们的bit数组现在看起来像这样:

0 0 1 1 0 0 1 0
指数 0 1 2 3. 4 5 6 7

太好了。注意,Alice和Bob最后都把6位设为1。一个哈希碰撞。不是一个问题。这就是为什么我们要使用多个哈希函数。

现在,让我们看看Bloom筛选器中是否存在某些内容。我们通过运行要通过相同的哈希函数检查的条目来实现这一点。如果我们运行Alice the Allomancer,我们得到2和6,就像前面一样,如果我们运行Bob the Barbarian,我们得到3和6,就像前面一样。这是预期的,因为哈希函数是确定性的。

对于Alice,我们检查第2位和第6位。它们都是1,所以Alice可能在Bloom过滤器中。对于Bob,我们检查第3位和第6位。同样的结果。

让我们检查一下是否有我们知道不在过滤器中的人,牧师Eric。Eric的哈希产生0和6。因为Eric的所有位都没有设置为1,所以他肯定不在Bloom过滤器中。

但是弗里茨战士呢?我们还没见到他。他的哈希值是2和3。第2位和第3位都被设为1,Alice设为2,Bob设为3。弗里茨没有被加入,但他可能在过滤器中。嗯。

这就是Bloom过滤器不确定性的来源。它可以通过增加位宽和/或哈希数来改善。执行这两种操作都会消耗更多的内存,所以总是要进行权衡。生活就是这样。

您可以使用一些代数和以下公式计算错误率,其中p为错误率,k是哈希数,是位宽度,并且n是期望插入的最大元素数:

p=(1 -ekn / mk

或者你可以使用在线计算器,比如这一个

从Bloom过滤器开始

布鲁姆滤镜还有很多我们没有讲到的。

我们探索了一个简单的用例,但还有很多。如果你有很多数据,不需要完善,Bloom筛选器会为你提供。你可以使用Bloom筛选器跟踪网络爬虫已经爬过的URL,或者记住社交媒体上的建议好友,这样用户就不会一直看到相同的“也许你知道”当他们已经说没有的时候,建议,或者只是把字典里的所有单词都喂进去,然后用它作为拼写检查。

我们也没有对Redis执行任何基准测试来比较集合和Bloom过滤器我们以前在博客上写过所以没有必要!

如果您想更深入地探索Bloom过滤器,请查看RedisBloom文档在Bloom滤镜上查看你可以用它们做的所有事情。如果你真的想内化它们是如何工作的,用你选择的语言写你自己的Bloom过滤器。请和我一起分享推特

Baidu