如何在 Redis 中存储唯一访问
How to store unique visits in Redis
我想知道有多少人访问了每个博客页面。为此,我在博客 table(MS SQL 数据库)中有一个专栏来保持总访问量。但我也希望访问尽可能独特。
所以我把用户的唯一Id和blog Id保存在Redis缓存中,每次用户访问一个页面,我都会检查她之前是否访问过这个页面,如果没有,我会增加总访问次数。
我的问题是,存储此类数据的最佳方式是什么?
目前,我创建了一个像这样的密钥“project-visit-{blogId}-{userId}”并使用 StringSetAsync 和 StringGetAsync。但是我不知道这种方法是否有效。
有什么想法吗?
您的解决方案不是原子的,除非您将 get 和 set 操作包装在事务或 Lua 脚本中。
更好的解决方案是将 project-visit-{blogId}-{userId}
保存到 Redis 集合中。当您拜访时,请致电 SADD
将项目添加到集合中。仅当用户之前未访问过此页面时,Redis 才会将新项目添加到集合中。如果要获取总数,只需调用 SCARD
即可获取集合的大小。
无论back-end技术(编程语言等)如何,都可以使用Redis流。这是 Redis 5 中的一项非常新的功能,允许您为在 Redis 中创建的主题(流)定义发布者和订阅者。然后,在每次用户访问中,您向该流提交一条新记录(当然是异步的)。您可以在该记录中保存任何您想要的信息(用户 ip、id 等)。
为每次唯一访问定义一个键根本不是一个好主意,因为:
- 这让redis GC的日子不好过
- 与 use-case 相比,性能无法与 Stream 相提并论,特别是如果您将该 redis 实例用于其他目的
- 不断收集这些唯一身份访问并对其进行处理效率不高。您必须始终扫描所有键
结论:
如果您想使用 Redis,请使用 Redis Stream。如果Redis可以改,肯定用Kafka(或类似的技术)。
如果可以牺牲一些精度,HyperLogLog (HLL) 概率数据结构是计算唯一访问次数的绝佳解决方案,因为:
- 它只使用 12K 内存,而且这些内存是固定的 - 它们不会随着唯一访问次数的增加而增长
- 您不需要存储用户数据,这让您的服务更加privacy-oriented
HyperLogLog 算法确实很聪明,但是你不需要了解它的内部工作原理就可以使用它,几年前 Redis 将它添加为一种数据结构。因此,作为用户,您需要知道的是,使用 HyperLogLogs,您可以计算固定内存 space 12K 中的唯一元素(访问),误差率为 0.81%.
假设您想统计每天的独立访问次数;您每天必须有一个 HyperLogLog,名称类似于 cnt:page-name:20200917
,每次用户访问页面时,您都会将它们添加到 HLL:
> PFADD cnt:page-name:20200917 {userID}
如果您多次添加同一个用户,他们仍然只会被计算一次。
要获得计数,您 运行:
> PFCOUNT cnt:page-name:20200917
您可以通过为不同的时间间隔设置不同的 HLL 来更改唯一用户的粒度,例如 cnt:page-name:202009
为 2020 年 9 月。
这个快速解释器很好地阐述了这一点:https://www.youtube.com/watch?v=UAL2dxl1fsE
此博客 post 也可能有帮助:https://redislabs.com/redis-best-practices/counting/hyperloglog/
如果您对 Antirez 的内部实现感到好奇,post 是一本很棒的读物:http://antirez.com/news/75
注意:请注意,使用此解决方案您会丢失访问该页面的用户的信息,您只有计数
我想知道有多少人访问了每个博客页面。为此,我在博客 table(MS SQL 数据库)中有一个专栏来保持总访问量。但我也希望访问尽可能独特。 所以我把用户的唯一Id和blog Id保存在Redis缓存中,每次用户访问一个页面,我都会检查她之前是否访问过这个页面,如果没有,我会增加总访问次数。
我的问题是,存储此类数据的最佳方式是什么? 目前,我创建了一个像这样的密钥“project-visit-{blogId}-{userId}”并使用 StringSetAsync 和 StringGetAsync。但是我不知道这种方法是否有效。
有什么想法吗?
您的解决方案不是原子的,除非您将 get 和 set 操作包装在事务或 Lua 脚本中。
更好的解决方案是将 project-visit-{blogId}-{userId}
保存到 Redis 集合中。当您拜访时,请致电 SADD
将项目添加到集合中。仅当用户之前未访问过此页面时,Redis 才会将新项目添加到集合中。如果要获取总数,只需调用 SCARD
即可获取集合的大小。
无论back-end技术(编程语言等)如何,都可以使用Redis流。这是 Redis 5 中的一项非常新的功能,允许您为在 Redis 中创建的主题(流)定义发布者和订阅者。然后,在每次用户访问中,您向该流提交一条新记录(当然是异步的)。您可以在该记录中保存任何您想要的信息(用户 ip、id 等)。
为每次唯一访问定义一个键根本不是一个好主意,因为:
- 这让redis GC的日子不好过
- 与 use-case 相比,性能无法与 Stream 相提并论,特别是如果您将该 redis 实例用于其他目的
- 不断收集这些唯一身份访问并对其进行处理效率不高。您必须始终扫描所有键
结论: 如果您想使用 Redis,请使用 Redis Stream。如果Redis可以改,肯定用Kafka(或类似的技术)。
如果可以牺牲一些精度,HyperLogLog (HLL) 概率数据结构是计算唯一访问次数的绝佳解决方案,因为:
- 它只使用 12K 内存,而且这些内存是固定的 - 它们不会随着唯一访问次数的增加而增长
- 您不需要存储用户数据,这让您的服务更加privacy-oriented
HyperLogLog 算法确实很聪明,但是你不需要了解它的内部工作原理就可以使用它,几年前 Redis 将它添加为一种数据结构。因此,作为用户,您需要知道的是,使用 HyperLogLogs,您可以计算固定内存 space 12K 中的唯一元素(访问),误差率为 0.81%.
假设您想统计每天的独立访问次数;您每天必须有一个 HyperLogLog,名称类似于 cnt:page-name:20200917
,每次用户访问页面时,您都会将它们添加到 HLL:
> PFADD cnt:page-name:20200917 {userID}
如果您多次添加同一个用户,他们仍然只会被计算一次。 要获得计数,您 运行:
> PFCOUNT cnt:page-name:20200917
您可以通过为不同的时间间隔设置不同的 HLL 来更改唯一用户的粒度,例如 cnt:page-name:202009
为 2020 年 9 月。
这个快速解释器很好地阐述了这一点:https://www.youtube.com/watch?v=UAL2dxl1fsE
此博客 post 也可能有帮助:https://redislabs.com/redis-best-practices/counting/hyperloglog/
如果您对 Antirez 的内部实现感到好奇,post 是一本很棒的读物:http://antirez.com/news/75
注意:请注意,使用此解决方案您会丢失访问该页面的用户的信息,您只有计数