Redis-08Redis数据结构--基数HyperLogLog

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Redis-08Redis数据结构--基数HyperLogLog

概述


基数是一种算法。


举个例子 , 一本英文著作由数百万个单词组成,你的内存却不足以存储它们,那么我们先分析一下业务。英文单词本身是有限的,在这本书的几百万个单词中有许许多多重复单词 ,扣去重复的单词,这本书中也就是几千到一万多个单词而己,那么内存就足够存储它们 了。比如数字集合{1,2,5,7,9, 1,5,9 }的基数集合为{ 1,2,5,7,9}那么基数(不重复元素)就是 5 , 基数的作用是评估大约需要准备多少个存储单元去存储数据,但是基数的算法一般会存在一定的误差(一般是可控的)。


Redis 对基数数据结构的支持是从版本 2.8.9 开始的。


基数并不是存储元素,存储元素消耗内存空间比较大,而是给某一个有重复元素的数据集合( 一般是很大的数据集合〉评估需要的空间单元数,所以它没有办法进行存储 ,加上在工作中用得不多 ,所以简要介绍一下 Redis的HyperLogLog 命令就可以了.


Redis 的 Hyperloglog 命令


官网:https://redis.io/commands#hyperloglog


image.png

127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> PFADD h1 a
(integer) 1
127.0.0.1:6379> PFADD h1 b
(integer) 1
127.0.0.1:6379> PFADD h1 c
(integer) 1
127.0.0.1:6379> PFADD h1 d
(integer) 1
127.0.0.1:6379> PFADD h1 a
(integer) 0
127.0.0.1:6379> PFADD h2 a
(integer) 1
127.0.0.1:6379> PFADD h2 z
(integer) 1
127.0.0.1:6379> PFMERGE h3 h1 h2
OK
127.0.0.1:6379> PFCOUNT h3
(integer) 5
127.0.0.1:6379> 


分析一下逻辑,首先往一个键为 h1的 HyperLogLog 插入元素 ,让其计算基数,到 了第 5 个命令“ pfadd h1 a”的时候,由于在此以前已经添加过,所以返回了 0。 它 的基数集合是{a,b,c,d},因此求集合长度是4 。


之后再添加第二个基数h2,它的基数是{a,z},所以在合并h1和h2到h3中的时候,它的基数和为{a,b,c,d,z}。所以求它的基数是5.


Spring 中操作基数


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath:redis/redis.properties" />
    <!--2,注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive而是maxTotal,而且没有maxWait属性,建议看一下Jedis源码或百度。 -->
    <!-- redis连接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--最大空闲数 -->
        <property name="maxIdle" value="${redis.maxIdle}" />
        <!--连接池的最大数据库连接数 -->
        <property name="maxTotal" value="${redis.maxTotal}" />
        <!--最大建立连接等待时间 -->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟) -->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 -->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 -->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />
      <property name="testOnBorrow" value="true"></property>
    <property name="testOnReturn" value="true"></property>
    <property name="testWhileIdle" value="true"></property>
    </bean>
  <!--redis连接工厂 -->
    <bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
        destroy-method="destroy">
        <property name="poolConfig" ref="jedisPoolConfig"></property>
        <!--IP地址 -->
        <property name="hostName" value="${redis.host.ip}"></property>
        <!--端口号 -->
        <property name="port" value="${redis.port}"></property>
        <!--如果Redis设置有密码 -->
        <property name="password" value="${redis.password}" /> 
        <!--客户端超时时间单位是毫秒 -->
        <property name="timeout" value="${redis.timeout}"></property>
        <property name="usePool" value="true" />
        <!--<property name="database" value="0" /> -->
    </bean>
  <!-- 键值序列化器设置为String 类型 -->
  <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
  <!-- redis template definition -->
  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
    p:connection-factory-ref="jedisConnectionFactory"
    p:keySerializer-ref="stringRedisSerializer"
    p:defaultSerializer-ref="stringRedisSerializer"
    p:valueSerializer-ref="stringRedisSerializer">
  </bean>
</beans>


package com.artisan.redis.baseStructure.hyperloglgo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
public class SpringRedisHyperLogLogDemo {
  @SuppressWarnings({ "unchecked", "rawtypes", "resource" })
  public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/spring-redis-hyperloglog.xml");
    RedisTemplate redisTemplate = (RedisTemplate) ctx.getBean("redisTemplate");
    // 为确保数据干净,先清除
    redisTemplate.delete("h1");
    redisTemplate.delete("h2");
    redisTemplate.delete("h3");
    // 添加指定元素到 HyperLogLog 中
    Long count = redisTemplate.opsForHyperLogLog().add("h1", "a", "b", "c", "d", "a");
    System.out.println(count);
    count = redisTemplate.opsForHyperLogLog().add("h2", "a");
    System.out.println(count);
    count = redisTemplate.opsForHyperLogLog().add("h2", "z");
    System.out.println(count);
    Long size = redisTemplate.opsForHyperLogLog().size("h1");
    System.out.println(size);
    Long size2 = redisTemplate.opsForHyperLogLog().size("h2");
    System.out.println(size2);
    Long size3 = redisTemplate.opsForHyperLogLog().union("h3", "h1", "h2");
    System.out.println(size3);
    Long size4 = redisTemplate.opsForHyperLogLog().size("h3");
    System.out.println(size4);
  }
}

输出

INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Thu Sep 27 00:11:19 CST 2018]; root of context hierarchy
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-hyperloglog.xml]
1
1
1
4
2
5
5



注意


使用 Spring 提供的 RedisTemplate 去展示多个命令可以学习到如何使用 RedisTemplate 操作 Redis 。 实际工作中并不是那么用的,因为每一 个操作会尝试从连接池里获取 一 个新的 Redis 连接,多个命令应该使用SessionCallback 接口进行操作 。


代码


代码托管到了 https://github.com/yangshangwei/redis_learn

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
相关文章
|
2月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
4月前
|
存储 NoSQL 算法
Redis设计与实现——数据结构与对象
Redis 是一个高性能的键值存储系统,其数据结构设计精妙且高效。主要包括以下几种核心数据结构:SDS、链表、字典、跳跃表、整数集合、压缩列表。此外,Redis 对象通过类型和编码方式动态转换,优化内存使用,并支持引用计数、共享对象和淘汰策略(如 LRU/LFU)。这些特性共同确保 Redis 在性能与灵活性之间的平衡。
|
7月前
|
NoSQL 算法 安全
Redis原理—1.Redis数据结构
本文介绍了Redis 的主要数据结构及应用。
Redis原理—1.Redis数据结构
|
9月前
|
存储 消息中间件 缓存
Redis 5 种基础数据结构?
Redis的五种基础数据结构——字符串、哈希、列表、集合和有序集合——提供了丰富的功能来满足各种应用需求。理解并灵活运用这些数据结构,可以极大地提高应用程序的性能和可扩展性。
163 2
|
10月前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
181 5
|
10月前
|
存储 NoSQL 关系型数据库
Redis的ZSet底层数据结构,ZSet类型全面解析
Redis的ZSet底层数据结构,ZSet类型全面解析;应用场景、底层结构、常用命令;压缩列表ZipList、跳表SkipList;B+树与跳表对比,MySQL为什么使用B+树;ZSet为什么用跳表,而不是B+树、红黑树、二叉树
|
10月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
213 59
|
3月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
42 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。