![三分钟了解Redis HyperLogLog 数据结构,Redis从入门到精通2023版,redis,数据结构,java,面试,后端,缓存](https://imgs.yssmx.com/Uploads/2023/07/572280-1.jpeg)
0. 前言
HyperLogLog算法是一种非常有用的数据结构和算法,它可以在很小的空间内估计一个集合中不重复元素的数量,具有很高的效率和精度,非常适合用于大数据量的计数场景。在使用时,需要注意HyperLogLog算法的概率特性,以及对误差的容忍度,才能得到最好的效果。
1. 原理
HyperLogLog是一种基数估计算法,它可以在只使用很少的内存空间的情况下,近似地估计一个集合中不重复元素的数量。
HyperLogLog算法的核心思想是将每个元素映射为一个二进制字符串,并对这个字符串进行一些特殊的处理,最终得到一个估计值,表示集合中不重复元素的数量。HyperLogLog算法的基本原理可以概括为以下几步:
-
对每个元素进行哈希运算,得到一个二进制字符串。
-
将这个二进制字符串分成若干个组(比如分成4个组),每个组的长度相等。
-
对每个组分别取最高位为1的位置(即从左往右数第一个为1的位置),得到一个索引值。
-
将这些索引值合并成一个二进制数,并将其转换为一个整数,作为集合中不重复元素的数量的估计值。
HyperLogLog算法的估计值的精度和存储空间有关,通常来说,可以通过调整存储空间的大小来控制估计值的误差率。HyperLogLog算法的误差率可以控制在0.81%以内。
下面是一个简单的实例,说明HyperLogLog算法的基本原理:
假设有一个集合S,包含以下元素:
S = {A, B, C, D, E, F, G, H}
首先,对每个元素进行哈希运算,得到一个二进制字符串:
A: 10101101
B: 11011101
C: 01101001
D: 01011011
E: 00111010
F: 10011011
G: 00111011
H: 00101101
然后,将这些二进制字符串分成若干个组,每个组的长度相等:
1010 1101
1101 1101
0110 1001
0101 1011
0011 1010
1001 1011
0011 1011
0010 1101
对每个组分别取最高位为1的位置,得到一个索引值:
4 3
4 3
2 4
2 3
1 3
4 3
1 3
1 4
将这些索引值合并成一个二进制数,并将其转换为一个整数,作为集合中不重复元素的数量的估计值:
11001010 -> 202
这个估计值可能会存在一定的误差,但是通过调整存储空间的大小,可以控制误差率在可接受的范围内。
HyperLogLog算法是一种非常有用的基数估计算法,它可以在很小的内存空间内,近似地估计一个集合中不重复元素的数量,具有很高的效率和精度,非常适合用于大数据量的计数场景。
Redis HyperLogLog是一种基于概率统计算法的数据结构,用于近似地计算一个集合中不重复元素的数量。它的优点是使用很少的内存来存储大量的元素,并且可以在不牺牲太多精度的情况下,快速地计算元素的数量。
HyperLogLog算法最初是由Philippe Flajolet和其它的研究人员提出的,它的核心思想是将每个元素映射为一个二进制字符串,并对这个字符串进行一些特殊的处理,最终得到一个估计值,表示集合中不重复元素的数量。在Redis中,HyperLogLog是通过一个专门的命令来实现的,这个命令的名称是PFADD,它可以用来添加元素到HyperLogLog中,还有一个命令PFCOUNT,可以用来获取HyperLogLog中不重复元素的数量。
HyperLogLog算法的核心技术是基数估计(cardinality estimation),它是一种通过概率统计算法来估计一个集合中不重复元素数量的方法。HyperLogLog算法的基本思路是对每个元素进行一些特殊的映射处理,然后将这些处理后的结果聚合起来,得到一个估计值。这个估计值的精度取决于使用的存储空间(即HyperLogLog的大小),通常来说,HyperLogLog的误差率可以控制在0.81%以内。
HyperLogLog算法的具体实现是比较复杂的,需要涉及到一些概率统计和数学知识。在Redis中,HyperLogLog算法是通过一些专门的数据结构和算法来实现的,这些数据结构和算法的底层实现是比较复杂的,但对于使用者来说,只需要掌握一些基本的命令即可。
在使用HyperLogLog算法时,需要注意以下几点:
-
HyperLogLog算法是一种概率算法,它的估计值可能会出现一定的误差,因此在使用时需要根据实际情况来选择HyperLogLog的大小,以及对误差的容忍度。
-
HyperLogLog算法只能用来估计集合中不重复元素的数量,不能用来判断具体的元素是否在集合中。
-
在使用PFADD命令向HyperLogLog中添加元素时,需要注意元素的唯一性,重复的元素不会被重复计算。
-
在使用PFCOUNT命令获取HyperLogLog中不重复元素的数量时,需要注意这个值是一个近似值,可能会存在一定的误差。
1.2 原理解析
HyperLogLog 是一种概率数据结构,它使用概率算法来统计集合的近似基数。而它算法的最本源则是伯努利过程。
伯努利过程就是一个抛硬币实验的过程。抛一枚正常硬币,落地可能是正面,也可能是反面,二者的概率都是 1/2 。伯努利过程就是一直抛硬币,直到落地时出现正面位置,并记录下抛掷次数 k。比如说,抛一次硬币就出现正面了,此时 k 为 1; 第一次抛硬币是反面,则继续抛,直到第三次才出现正面,此时 k 为 3。
(备注:有特殊公式符号,无法显示,所以截图贴在该位置)
下面,我们就来讲解一下 HyperLogLog 是如何模拟伯努利过程,并最终统计集合基数的。
HyperLogLog 在添加元素时,会通过 Hash 函数,将元素转为 64 位比特串,例如输入 5,便转为 101(省略前面的 0,下同)。这些比特串就类似于一次抛硬币的伯努利过程。比特串中,0 代表了抛硬币落地是反面,1 代表抛硬币落地是正面,如果一个数据最终被转化了 10010000,那么从低位往高位看,我们可以认为,这串比特串可以代表一次伯努利过程,首次出现 1 的位数为 5,就是抛了 5 次才出现正面。
所以 HyperLogLog 的基本思想是利用集合中数字的比特串第一个 1 出现位置的最大值来预估整体基数,但是这种预估方法存在较大误差,为了改善误差情况,HyperLogLog 中引入分桶平均的概念,计算 m 个桶的调和平均值。
Redis 中 HyperLogLog 一共分了 2^14 个桶,也就是 16384 个桶。每个桶中是一个 6 bit 的数组,如下图所示。
HyperLogLog 将上文所说的 64 位比特串的低 14 位单独拿出,它的值就对应桶的序号,然后将剩下 50 位中第一次出现 1 的位置值设置到桶中。50 位中出现 1 的位置值最大为 50,所以每个桶中的 6 位数组正好可以表示该值。
在设置前,要设置进桶的值是否大于桶中的旧值,如果大于才进行设置,否则不进行设置。示例如下图所示。
此时为了性能考虑,是不会去统计当前的基数的,而是将 HyperLogLog 头的 card 属性中的标志位置为 1,表示下次进行 pfcount 操作的时候,当前的缓存值已经失效了,需要重新统计缓存值。在后面 pfcount 流程的时候,发现这个标记为失效,就会去重新统计新的基数,放入基数缓存。
在计算近似基数时,就分别计算每个桶中的值,带入到上文将的 DV 公式中,进行调和平均和结果修正,就能得到估算的基数值。
2.实战案例
假设有一个在线商城系统,需要统计每个商品浏览量的数量,以便进行商品推荐和排序等操作。如果使用传统的数据结构和算法,需要使用一个哈希表来存储每个商品的浏览量,这样会占用大量的内存空间。而使用Redis HyperLogLog算法,可以使用很少的内存来存储大量的商品浏览量,并且可以快速地估计每个商品的浏览量。
下面是一个使用Spring Boot和Redis HyperLogLog算法实现商品浏览量统计的例子:
首先,在pom.xml文件中添加Redis和Spring Data Redis的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
然后,在application.properties文件中配置Redis的连接信息:
spring.redis.host=localhost
spring.redis.port=6379
接着,定义一个RedisTemplate的Bean,用于操作Redis:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
然后,定义一个商品浏览量服务类,用于实现商品浏览量的统计和查询:
@Service
public class ProductViewService {
private final RedisTemplate<String, Object> redisTemplate;
@Autowired
public ProductViewService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void addViewCount(String productId, String userId) {
String key = "product:" + productId + ":view";
String value = userId;
redisTemplate.opsForHyperLogLog().add(key, value);
}
public long getViewCount(String productId) {
String key = "product:" + productId + ":view";
return redisTemplate.opsForHyperLogLog().size(key);
}
}
在addViewCount方法中,使用PFADD命令向HyperLogLog中添加元素,元素的值为userId,表示这个用户浏览了这个商品。在getViewCount方法中,使用PFCOUNT命令获取HyperLogLog中不重复元素的数量,即为这个商品的浏览量。
最后,在商品详情页中调用addViewCount方法,统计商品的浏览量,在商品列表页中调用getViewCount方法,显示每个商品的浏览量。
@GetMapping("/product/detail/{id}")
public String productDetail(@PathVariable("id") String id, HttpSession session) {
String userId = session.getId();
productViewService.addViewCount(id, userId);
// other logic
}
@GetMapping("/product/list")
public List<Product> productList() {
List<Product> products = productService.getProducts();
for (Product product : products) {
long viewCount = productViewService.getViewCount(product.getId());
product.setViewCount(viewCount);
}
return products;
}
这样,就可以使用Redis HyperLogLog算法来实现商品浏览量的统计和查询,大大地节省了内存空间,并且能够快速地获取每个商品的浏览量。
3. Redis从入门到精通系列文章
《Redis 从入门到精通【进阶篇】之高可用哨兵机制(Redis Sentinel)详解》
《Redis 从入门到精通【进阶篇】之redis主从复制详解》
《Redis 从入门到精通【进阶篇】之Redis事务详解》
《Redis从入门到精通【进阶篇】之对象机制详解》
《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
《Redis从入门到精通【进阶篇】之持久化 AOF详解》
《Redis从入门到精通【进阶篇】之持久化RDB详解》
《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
4. 常见问题
根据上面的学习,我们来简答一下常见的Redis数据结构相关面试题
Redis HyperLogLog是一个非常有用的基数估计算法,常常在面试中被提及。下面是一些常见的Redis HyperLogLog面试题及其答案:
4.1. 什么是Redis HyperLogLog?
答:Redis HyperLogLog是一种基数估计算法,它可以在只使用很少的内存空间的情况下,近似地估计一个集合中不重复元素的数量。
4.2. HyperLogLog算法的核心思想是什么?
答:HyperLogLog算法的核心思想是将每个元素映射为一个二进制字符串,并对这个字符串进行一些特殊的处理,最终得到一个估计值,表示集合中不重复元素的数量。
4.3. HyperLogLog算法的误差率如何控制?
答:HyperLogLog算法的误差率可以通过调整存储空间的大小来控制,通常来说,可以将存储空间的大小设置为2^b个字节,其中b是一个整数,表示二进制字符串的长度。
4.4. HyperLogLog算法的存储空间大小与误差率的关系是怎样的?
答:HyperLogLog算法的存储空间大小与误差率成反比例关系,存储空间越大,误差率越小。
4.5. HyperLogLog算法在Redis中如何实现?
答:在Redis中,可以使用PFADD命令向HyperLogLog中添加元素,使用PFCOUNT命令获取HyperLogLog中不重复元素的数量。
4.6. HyperLogLog算法有什么应用场景?
答:HyperLogLog算法可以用于统计网站的UV(Unique Visitor)和PV(Page View)等指标,也可以用于计算各种统计分析数据,如用户活跃度、广告点击率等。
4.7. HyperLogLog算法的优缺点是什么?
答:HyperLogLog算法的优点是可以在很小的内存空间内,近似地估计一个集合中不重复元素的数量,具有很高的效率和精度,非常适合用于大数据量的计数场景。缺点是HyperLogLog算法只能估计不重复元素的数量,而不能精确地计算,误差率也受到存储空间大小的限制。
4.8. Redis HyperLogLog与Bloom Filter有什么区别?
答:Redis HyperLogLog和Bloom Filter都是基数估计算法,但是它们的应用场景略有不同。HyperLogLog算法适用于估计集合中不重复元素的数量,而Bloom Filter算法则适用于判断一个元素是否属于某个集合。此外,HyperLogLog算法的误差率可以控制在一个比较小的范围内,而Bloom Filter算法的误差率会随着存储空间大小的增加而逐渐减小。
4.9. Redis HyperLogLog如何处理哈希冲突?
答:Redis HyperLogLog使用Hash函数对元素进行哈希运算,得到一个哈希值。如果发生哈希冲突,HyperLogLog会选择一个最小的值作为索引位置。这样做可以保证估计值的误差率不会超过预设的误差率。文章来源:https://www.toymoban.com/news/detail-572280.html
4.10. Redis HyperLogLog的优化策略有哪些?
答:Redis HyperLogLog的优化策略主要有两个:稀疏化和交集估计。稀疏化是指对于一些元素数量较少的HyperLogLog,可以使用较小的存储空间来存储,从而节省内存空间。交集估计是指对于多个HyperLogLog的交集,可以使用一些特殊的算法来估计交集的大小,从而减小误差率。大家好,我是冰点,今天的HyperLogLog 数据结构详解,全部内容就是这些。如果你有疑问或见解可以在评论区留言。文章来源地址https://www.toymoban.com/news/detail-572280.html
到了这里,关于三分钟了解Redis HyperLogLog 数据结构的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!