学习

使用 Redis 防止重复签到

Simon Prickett
作者
Simon Prickett, Redis 首席开发者倡导者

随着我们应用程序的普及,我们从不断扩展的用户群中获得越来越多的签到。我们决定对这一点进行一些限制,只允许每个用户对每个位置进行一次特定星级评定。例如,如果用户 100 在位置 73 签到并将其评为 3 星,我们希望拒绝他们对该位置的任何其他 3 星签到。

为了做到这一点,我们需要一种方法来记住每个签到,并快速确定我们是否以前见过它。我们不能通过查询签到流中的数据来做到这一点,因为流不允许这种类型的访问,并且会定期修剪,删除已处理且不再需要的较旧签到。

我们可以将我们的签到表示为 <userId>:<locationId>:<rating> 格式。使用此模式,字符串 100733 代表用户 100 在位置 73 签到的 3 星评级。

然后我们需要记住每个签到,以便我们可以确保它是用户 ID、位置 ID 和星级的唯一组合。我们可以为此使用 Redis 集合。集合非常适合我们想要维护一组唯一成员的情况,因为它们不允许重复。使用 Redis 集合,添加新成员和检查成员是否在集合中都是 O(1) 操作,这意味着它们的性能不会随着集合的增长而下降。

但是,我们添加的每个新集合成员(在本例中,它们是唯一的签到)都会导致集合在 Redis 服务器上占用更多内存。随着我们继续接收越来越多的签到,这种增长将成为一个问题。

但如果有一种方法可以检查潜在的新成员是否已经在集合中,而不会出现这种内存消耗问题呢?布隆过滤器是一种空间高效的概率数据结构,可以在这里提供帮助。布隆过滤器存储集合成员的哈希表示,而不是实际的成员数据本身。与集合不同,我们无法从布隆过滤器中取回成员,但我们可以测试某件事是否已经在其中... 由于哈希冲突,可能会出现一些误报。当被问及某件事是否是集合的成员时,布隆过滤器可以告诉我们“不,它不是”,或者“它可能是”。

这种哈希方法牺牲了使用集合获得的 100% 准确性,以大幅减少内存开销。布隆过滤器可以配置为具有所需的可接受错误率,因此对于我们的应用程序,这似乎是执行“无重复签到”规则的好方法,而不会出现内存消耗失控的问题。每当布隆过滤器告诉我们它可能以前见过签到时,它通常都是正确的,我们接受有时我们不允许我们实际上以前没有进行的签到,作为一种合理的折衷方案,以控制我们的内存使用量。

Redis Stack 为 Redis 提供了一个布隆过滤器实现,以及其他有用的概率数据结构。在视频中,您将看到在 Node.js 应用程序中使用它非常容易,不需要任何数学技能!

动手练习#

在本练习中,您将看到布隆过滤器的实际作用,方法是尝试多次向系统提交相同的签到。

您需要运行签到接收器服务... 如果它仍然从之前的练习中运行,请使用 Ctrl-C 停止它。然后,使用以下命令重新启动它。此命令将禁用我们不希望在本练习中使用的登录要求

$ npm run checkinreceiver

> js-crash-course@0.0.1 checkinreceiver
> node ./src/checkinreceiver.js

info: Authentication disabled, checkins do not require a valid user session.
info: Checkin receiver listening on port 8082.

现在,打开 Postman 并创建一个新请求,选择“POST”作为 HTTP 动词。

  • 将 URL 设置为 localhost:8082/api/checkin
  • 在“正文”选项卡中,将类型下拉菜单设置为“原始”和“JSON”。
  • 在正文文本区域中,输入以下 JSON:
{ "userId": 100, "locationId": 73, "starRating": 3 }

您的请求应该如下所示

单击“发送”将您的签到提交到签到接收器,签到接收器应该以 202 已接受状态和空响应主体进行响应

再次单击“发送”,您应该会收到签到接收器发来的 422 不可处理实体响应以及错误消息

在签到接收器服务仍在运行的情况下,启动生成随机签到的签到生成器实用程序

node-js-crash-course $ npm run checkingenerator

> js-crash-course@0.0.1 checkingenerator
> node ./src/checkingenerator.js

info: Started checkin generator.

让签到生成器继续运行。它每隔几秒钟就会生成一个新的随机签到。让它运行并生成数百个签到。在运行的过程中,使用 redis-cli 或 RedisInsight 中的 CLI 选项卡定期监控布隆过滤器所需的内存使用量

127.0.0.1:6379> bf.info ncc:checkinfilter
 1) Capacity
 2) (integer) 1000000
 3) Size
 4) (integer) 2576760
 5) Number of filters
 6) (integer) 1
 7) Number of items inserted
 8) (integer) 269
 9) Expansion rate
10) (integer) 2

随着生成更多签到,运行此操作几次,并注意存储布隆过滤器所需的大小不会随着插入的项目数量的增加而增加。虽然牺牲了一些准确性,但布隆过滤器是这种用例的存储高效解决方案。

外部资源#

在本视频中,Guy Royse 解释了什么是布隆过滤器以及如何在 Redis 中使用它们

Redis 集合是一种强大的数据类型,值得了解,使用 Andrew 在 Redis 大学 YouTube 频道上的两个视频了解更多信息。首先,解释 Redis 集合

然后是 Redis 集合详解