巧用 Redis Hyperloglog,轻松统计 UV 数据
本文由 yanglbme 原创,首发于公众号“Doocs开源社区”,欢迎转载。
如果你正在开发一个基于“事件”的应用程序,该应用程序可以处理来自不同用户的许多请求,那么你很大可能希望能够计算滑动窗口或指定时间范围内不同的用户操作。
计数不同用户行为的最快方法之一是写一个类似 SELECT COUNT(DISTINCT user) 的 SQL。但是,如果实时数据的量达到了上百万条,这可能会很昂贵。你可能会想到另一种方法,就是将用户保存在一个 Redis set 集合中,因为 set 天然具备去重的功能。
但是,这种解决方案也带来了它固有的问题。如果一个统计不同用户记录的应用程序运行有多个实例,那么我们需要具有巨大 RAM 大小的内存缓存解决方案。如果要处理 1000 万个不同的记录,每个记录分配 10 字节,那么仅在一个时间范围内我们就至少需要 100MB 的内存。因此,这不是内存有效的解决方案。
在本文中,我想向你展示如何通过在 Redis Cache 服务器中分配少于 2MB 的内存来处理一百万个不同的用户记录。
我们都知道,Redis 有好几种数据结构,比如:String、BitMap、Set、Sorted Set等。在这里我想特别强调一下Hyperloglog,因为它最适合通过减少内存消耗来统计不同的用户操作。
![986864afad990079ee5634ffc3a69687.png](https://i-blog.csdnimg.cn/blog_migrate/ed66ff50dc17755c5292df42847588f2.jpeg)
Hyper LogLog
Hyper LogLog 计数器的名称是具有自描述性的。 你可以仅仅使用loglog(Nmax)+ O(1)位来估计基数为 Nmax 的集合的基数。
Redis Hyperloglog 操作
要进行 Redis Hyperloglog 的操作,我们可以使用以下三个命令:
- PFADD
- PFCOUNT
- PFMERGE
我们用一个实际的例子来解释这些命令。比如,有这么个场景,用户登录到系统,我们需要在一小时内统计不同的用户。 因此,我们需要一个 key,例如 USER:LOGIN:2019092818。 换句话说,我们要统计在 2019 年 09 月 28 日下午 18 点至 19 点之间发生用户登录操作的非重复用户数。对于将来的时间,我们也需要使用对应的 key 进行表示,比如 2019111100、2019111101、2019111102 等。
我们假设,用户 A、B、C、D、E 和 F 在下午 18 点至 19 点之间登录了系统。
127.0.0.1:6379> pfadd USER:LOGIN:2019092818 A(integer) 1127.0.0.1:6379> pfadd USER:LOGIN:2019092818 B C D E F(integer) 1127.0.0.1:6379>复制代码
当进行计数时,你会得到预期的 6。
127.0.0.1:6379> pfcount USER:LOGIN:2019092818(integer) 6复制代码
如果 A 和 B 在这个时间内多次登录系统,你也将得到相同的结果,因为我们仅保留不同的用户。
127.0.0.1:6379> pfadd USER:LOGIN:2019092818 A B(integer) 0127.0.0.1:6379> pfcount USER:LOGIN:2019092818(integer) 6复制代码
如果用户 A~F 和另外一个其他用户 G 在下午 19 点至下午 20 点之间登录系统:
127.0.0.1:6379> pfadd USER:LOGIN:2019092819 A B C D E F G(integer) 1127.0.0.1:6379> pfcount USER:LOGIN:2019092819(integer) 7复制代码
现在,我们有两个键 USER:LOGIN:2019092818 和 USER:LOGIN:2019092819,如果我们想知道在 18 点到 20 点(2 小时)之间有多少不同的用户登录到系统中,我们可以直接使用pfcount命令对两个键进行合并计数:
127.0.0.1:6379> pfcount