如何在Redis中存储唯一访问量

4
我想知道每个博客页面的访问人数。因此,在Blogs表(MS SQL DB)中,我有一个列来保存总访问次数。但我也希望访问尽可能唯一。 所以,我将用户的唯一ID和博客ID存储在Redis缓存中,每当用户访问页面时,我检查她是否曾经访问过此页面,如果没有,我将增加总访问计数。
我的问题是,什么是存储这类数据的最佳方法? 目前,我创建像“project-visit-{blogId}-{userId}”这样的键,并使用StringSetAsync和StringGetAsync。 但我不知道这种方法是否高效。
有什么想法吗?

你考虑过使用cookie吗?你可以用它来引用缓存中的数据。 - Mehrdad Hedayati
不,用户可以清除她的缓存并访问一个页面一百次。 - Maddie
3个回答

6
如果你可以牺牲一些精度,HyperLogLog(HLL)概率数据结构是计数唯一访问的一个很好的解决方案,因为:
  • 它只使用12K的内存,并且这些内存是固定的-它们不会随着唯一访问次数而增长
  • 你不需要存储用户数据,这使得你的服务更加注重隐私保护
HyperLogLog算法非常聪明,但你不需要理解其内部工作原理就能使用它。几年前Redis将其添加为一种数据结构。因此,作为用户,你只需要知道:使用HyperLogLogs,你可以在固定的12K内存空间中计数唯一元素(访问),其误差率仅为0.81%
假设你想每天记录唯一访问量;你需要每天拥有一个HyperLogLog,命名为像 `cnt:page-name:20200917` 这样的东西,并且每次用户访问页面时,你将把它们添加到HLL中:
> PFADD cnt:page-name:20200917 {userID}

如果您重复添加同一个用户,它们仍将只被计算一次。若要获取计数,请执行以下操作:

> PFCOUNT cnt:page-name:20200917

您可以通过为不同的时间间隔使用不同的HLLs来更改唯一用户的粒度,例如cnt:page-name:202009表示2020年9月。
这个快速说明很清楚: https://www.youtube.com/watch?v=UAL2dxl1fsE 这篇博客文章也可能有所帮助:https://redislabs.com/redis-best-practices/counting/hyperloglog/ 如果您对内部实现感到好奇,Antirez的发布文章是一个很好的阅读材料:http://antirez.com/news/75 注意:请注意,使用此解决方案会丢失访问该页面的哪些用户的信息,您只能得到计数。

它有内存限制吗?我可以为一个页面拥有一个日志,记录该页面的所有访问记录,无论时间如何? - Maddie
1
HLL 有元素数量限制,但非常高,远远超过地球上的人数,所以你应该没问题。 :) 如果您不需要任何细节,可以为页面使用单个 HLL,并显示自创建以来的唯一访问次数(即 HLL)。 - Elena Kolevska

1
您的解决方案不是原子性的,除非您将获取和设置操作包装在事务或Lua脚本中。
更好的解决方案是将“project-visit-{blogId}-{userId}”保存到Redis集合中。当有访问时,调用“SADD”将项目添加到集合中。Redis仅在用户首次访问此页面时将新项目添加到集合中。如果您想获取总数,只需调用“SCARD”以获取集合的大小即可。

0
无论后端技术(编程语言等)如何,您都可以使用Redis Stream。这是Redis 5中非常新的功能,允许您定义发布者和订阅者到在Redis中创建的主题(流)。然后,在每个用户访问中,您都会将一个新记录(当然是异步的)提交到此流中。您可以在该记录中保存任何信息(用户IP、ID等)。
为每个唯一访问定义一个键根本不是一个好主意,因为:
- 它使Redis GC更加困难 - 性能与使用案例相比,与Stream不可比较,特别是如果您将Redis实例用于其他目的 - 不断收集这些唯一访问并处理它们是低效的。您必须始终扫描所有键
结论: 如果要使用Redis,请使用Redis Stream。如果可以更改Redis,则一定要选择Kafka(或类似技术)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接