Spring Data Redis 教程

HyperLogLogOperations 操作接口

前面介绍了 Spring Data Redis 对 Redis 中 value、hash、set、zset 数据类型的操作。下面将介绍怎样利用 Spring Data Redis 实现对 HyperLogLog 数据类型的操作。

注意:Redis 在 2.8.9 版本添加了 HyperLogLog 结构。

业务场景

如果我们需要去统计APP接口被客户端请求的次数。如果使用 Redis 去实现该业务,我们最容易想到的就是使用 key=value 保存,key 保存请求接口,value 保存次数。然后使用,Redis 的 incr、incrby 等指令进行递增操作。如果存在多个请求接口,则使用多个 key=value 进行保存就是。

如果我们需要统计接口被客户端请求的次数,一个客户端多次请求只算一次?在 Java 里面的数据结构必然会想到Set(因为 Set 不允许有重复元素)。幸运的是,Redis 里面也有set。但是,对于小项目来说,客户端数量不多,数据量也不会很庞大,这是可行的。但是对大项目来说呢?这个 set 只会无限扩张,最后变得很庞大,非常占用内存,变得很臃肿。

问题来了,那么如何既能够实现该功能,又可以减少内存的浪费呢,可以考虑使用 Redis 的 HyperLoglog。

什么是 HyperLogLog?

Redis HyperLogLog 是用来做基数统计的算法。HyperLogLog 的优点是在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。

因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

什么是基数?

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

方法定义

不像其他 Operations 操作接口定义很多方法,该 Operations 只定义了下面四个方法:

  • Long add(K key, V... values) 将给定一个或多个值添加到键。

  • void delete(K key) 删除给定的键。

  • Long size(K... keys) 获取键中的当前元素数。

  • Long union(K destination, K... sourceKeys) 将 sourceKeys 键的所有值合并到 destination 键中。

示例

示例一

向 Redis 键为 hyper_log_log 的 hyperloglog 中随机写入 1000 次介于 0~99 之间的数,然后获取 hyper_log_log 键中元素的个数。代码如下:

HyperLogLogOperations<String,String> ops = redisTemplate.opsForHyperLogLog();
for(int i = 0; i < 1000; i++) {
    String val = String.valueOf((int)(Math.random() * 100));
    ops.add("hyper_log_log", val);
}

Long size = ops.size("hyper_log_log");
System.out.println("size=" + size);

运行示例,输出如下:

size=100

从输出可知,hyperloglog 自动帮我们进行了元素去重,效果和 set 类似,只是使用 hyperloglog 占用内存少,减少内存浪费。

示例二

分别向 hyper_log_log1 和 hyper_log_log2 两个键中随机写入 100 个介于 0~99 的元素,然后使用 union() 方法合并 hyper_log_log1 和 hyper_log_log2 两个键,返回合并后去重元素的个数。代码如下:

Long size;
HyperLogLogOperations<String,String> ops = redisTemplate.opsForHyperLogLog();
// 初始化数据
for(int i = 0; i < 100; i++) {
    String val = String.valueOf((int)(Math.random() * 100));
    ops.add("hyper_log_log1", val);
}
size = ops.size("hyper_log_log1");
System.out.println("hyper_log_log1 size=" + size);

for(int i = 0; i < 100; i++) {
    String val = String.valueOf((int)(Math.random() * 100));
    ops.add("hyper_log_log2", val);
}
size = ops.size("hyper_log_log2");
System.out.println("hyper_log_log2 size=" + size);

// 合并 hyper_log_log1 和 hyper_log_log2 两个键的元素,获取去重后元素个数
size = ops.union("hyper_log_log1", "hyper_log_log2");
System.out.println("union size=" + size);

运行示例,输出如下:

hyper_log_log1 size=66
hyper_log_log2 size=64
union size=90

从输出结果可知,union() 方法是将两个 hyperloglog 进行了合并,并且去掉了重复的元素,然后返回不重复元素个数。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号