Redis怎么使用HyperLogLog实现

1. 概述

Redis 在 2.8.9 版本添加了 HyperLogLog 数据结构,用来做基数统计,其优点是在输入元素的数量非常大时,计算基数所需的空间比较小并且一般比较恒定。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,并不会储存输入元素本身,所以 HyperLogLog 不能像集合那样能返回输入的各个元素。

2. 什么是基数?

比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。基数估计就是在误差可接受的范围内,快速计算基数。

3. 命令

用RedisHyperLogLog实现实时统计问题方案

目前只有 PFADD、PFCOUNT 和 PFMERGE 三个命令被 HyperLogLog 支持。我们先来逐一介绍一下。

3.1 PFADD

最早可用版本:2.8.9。时间复杂度:O(1)。

PFADD 命令可以将元素(可以指定多个元素)添加到 HyperLogLog 数据结构中,存储到第一个参数 key 指定的键中。如果基数估计(评估的元素个数)发生变化,返回1,否则返回0,即在执行命令后确认基数估计是否已变化。如果指定的 key 不存在,那么就创建一个空的 HyperLogLog 数据结构(即,指定字符串长度以及编码的 Redis String)。也可以调用不指定元素参数而只指定键的命令。如果键存在,不执行任何操作并返回 0;如果键不存在,则会创建一个新的 HyperLogLog 数据结并且返回 1。实质上仅仅是生成一个新的 HyperLogLog 数据结构,而不储存任何元素。

(1) 语法格式:

PFADD key element [element ...]

(2) 返回值:

整型,如果至少有个元素被添加返回 1,否则返回 0。

(3) Example:

127.0.0.1:6379>
PFADD hll a b c d e f g
(integer) 1
127.0.0.1:6379>
pfcount hll
(integer) 7 3.2 PFCOUNT

最早可用版本:2.8.9。时间复杂度:O(1),对于多个比较大的key的时间复杂度是O(N)。

使用PFCOUNT命令可以得到一个HyperLogLog估算基数的值(也就是元素的数量)。如果键不存在,该命令返回 0,否则返回该键的基数估算值。对于多个键,返回的是多个 HyperLogLog 并集的基数估算值,通过将多个 HyperLogLog 合并为一个临时的 HyperLogLog 计算基数估算值。使用极少且一贯的内存量,HyperLogLog 可以计算集合的唯一元素数量。每个 HyperLogLog 只用 12K 加上键本身的几个字节。

(1) 语法格式:

PFCOUNT key [key ...]

(2) 返回值:

整数,返回指定 HyperLogLog 的基数估算值,如果多个 HyperLogLog 则返回并集的基数估算值。

(3) Example:

127.0.0.1:6379>
PFADD hll foo bar zap
(integer) 1
127.0.0.1:6379>
PFADD hll zap zap zap
(integer) 0
127.0.0.1:6379>
PFADD hll foo bar
(integer) 0
127.0.0.1:6379>
PFCOUNT hll
(integer) 3
127.0.0.1:6379>
PFADD some-other-hll 1 2 3
(integer) 1
127.0.0.1:6379>
PFCOUNT some-other-hll
(integer) 3
127.0.0.1:6379>
PFCOUNT hll some-other-hll
(integer) 6

(4) 限制:

HyperLogLog 返回的结果并不精确,错误率大概在 0.81% 左右。

使用这个命令将会改变 HyperLogLog,并且使用 8 个字节来存储上一次计算的基数。所以,从技术角度来讲,PFCOUNT 是一个写命令。

(5) 性能问题

即使理论上处理一个密集型 HyperLogLog 需要花费较长时间,但是当只指定一个键时,PFCOUNT 命令仍然具有很高的性能。这是因为 PFCOUNT 会缓存上一次计算的基数,并且这个基数并不会一直变动,因为 PFADD 命令大多数情况下不会更新寄存器。所以才可以达到每秒上百次请求的效果。

当使用 PFCOUNT 命令处理多个键时,会对 HyperLogLog 进行合并操作,这一步非常耗时,更重要的是通过计算出来的并集的基数是不能缓存的。使用多个键时,PFCOUNT 的执行可能需要花费一些时间(通常为毫秒级),因此建议不要过度使用。

需要注意的是,该命令的单键和多键执行语义是不同的并且具有不同的性能。不建议过多使用多键执行语义。

3.3 PFMERGE

最早可用版本:2.8.9。时间复杂度:O(N),N是要合并的HyperLogLog的数量。

多个 HyperLogLog 可以通过 PFMERGE 命令合并成一个 HyperLogLog。合并后的 HyperLogLog 的基数估算值是通过对所有给定 HyperLogLog 进行并集计算得出的。计算完的结果保存到指定的键中。

语法格式:

PFMERGE destkey sourcekey [sourcekey ...]

返回值:

返回 OK。

Example:

127.0.0.1:6379>
PFADD hll1 foo bar zap a
(integer) 1
127.0.0.1:6379>
PFADD hll2 a b c foo
(integer) 1
127.0.0.1:6379>
PFMERGE hll3 hll1 hll2
OK
127.0.0.1:6379>
PFCOUNT hll3
(integer) 6

Redis 是一个受欢迎的开源字典中间件,它使用内存存储和持久性。它支持多种数据结构,其中 HyperLogLog 是一种数据结构,用于估计基数或独特元素的数量,而不需要存储每个元素。 在本文中,我们将探讨如何使用 Redis HyperLogLog 实现实时统计问题方案。
HyperLogLog 的简介
HyperLogLog 是一种处理多集合元素的算法,它可以在合理的空间占用下估计基数值。在传统的方法中,如果有一亿个元素,我们可能需要使用一个包含一亿个位的集合,这将需要至少几十兆字节的空间。而 HyperLogLog 则只需要占用数千个字节的空间,就可以估计出这些元素的基数。
HyperLogLog 通过使用哈希函数将元素分散到不同的桶中,从而估计基数。对于每个桶,它都会存储这个桶内元素的最大 hash 值的位数,之后通过某些计算,可以计算出所有桶的并集的估计基数。虽然 HyperLogLog 可能会有一些误差,但这个误差很小,一般不会超过 0.81%。
使用 Redis HyperLogLog实现统计
Redis 的 HyperLogLog 数据结构是在 2.8.9 版本中引入的。我们可以使用 Redis 命令来创建 HyperLogLog:
```
PFADD key element [element...]
```
该命令将元素添加到 HyperLogLog 数据结构中。例子:
```
PFADD hyperloglog a b c d e
```
使用 PFADD 命令,我们可以向一个 HyperLogLog 中添加多个元素。在这个例子中,我们将添加元素 a,b,c,d 和 e 到 HyperLogLog 数据结构中。
我们也可以使用 PFADD 命令来添加不同的数据类型。例如,可以使用 PFADD 命令将哈希值添加到 HyperLogLog 中:
```
PFADD hyperloglog $(echo -n \"a\" | sha256sum | awk '{ print $1 }')
```
该命令使用 sha256 算法对 ‘a’ 进行哈希,并将哈希值添加到 HyperLogLog 中。
一旦我们将元素添加到 HyperLogLog 中,我们可以使用以下命令查看基数:
```
PFCOUNT key [key...]
```
例子:
```
PFCOUNT hyperloglog
```
使用 PFCOUNT 命令,我们可以查看 HyperLogLog 数据结构中估计的基数。在这个例子中,我们将查看 HyperLogLog 数据结构中元素的估计数量。
HyperLogLog 的优缺点
HyperLogLog 优点:
- 具有很好的空间优化和速度优化;
- 可以使用较少的内存估算非常大的数据集;
- 可以分布式实现,不受单机内存限制。
HyperLogLog 缺点:
- 在小数据集上可能不如传统的 hash-based 去重方法。
- 在保证准确性的同时,需要选择合适的桶数量和哈希函数。如果选择不当,则会浪费大量内存或者导致基数计算不准确。
结论
Redis HyperLogLog 是一种功能强大的数据结构,可用于处理大型、高基数的数据集。 HyperLogLog 已经在 Redis 数据库中得到实现,并且可以非常方便地使用 Redis 进行查询和分析。此外,Redis HyperLogLog 样本操作适用于分布式系统,可以为其它无法在内存中完成的数据统计流程提供更高效的缓存方案。