当前位置: 首页 > article >正文

Java阶段四06

第4章-第6节

一、知识点

geospatial、hyperloglog、bitmap、事务、Jedis、SpringBoot集成Redis

二、目标

  • 了解三种特殊数据类型的使用

  • 理解什么是Redis事务

  • 学会使用Redis事务

  • 掌握使用JAVA代码操作Redis

三、内容分析

  • 重点

    • 理解什么是Redis事务

    • 学会使用Redis事务

    • 掌握使用JAVA代码操作Redis

  • 难点

    • 理解什么是Redis事务

    • 掌握使用JAVA代码操作Redis

四、内容

1、三种特殊数据类型

1.1 geospatial 地理位置

用来记录地理位置信息,两地之间的距离,范围等

  • GEOADD

    将指定的地理空间位置(纬度、经度、名称)添加到指定的key

    经纬度不能错,错了就加不进去了

    GEOADD china:city 119.27345 26.04769 fuzhou # 添加地址
    GEOADD china:city 118.613 24.88946 quanzhou # 添加地址
    GEOADD china:city 118.03394 24.48405 xiamen 113.27324 23.15792 guangzhou # 添加多条

  • GEOPOS

    key里返回所有给定位置元素的位置(经度和纬度)。

    GEOPOS china:city fuzhou # 查询
    GEOPOS china:city fuzhou quanzhou # 查询多条

  • GEODIST

    返回两个给定位置之间的距离。

    如果两个位置之间的其中一个不存在, 那么命令返回空值。

    指定单位的参数 unit 必须是以下单位的其中一个:

    • m 表示单位为米。

    • km 表示单位为千米。

    • mi 表示单位为英里。

    • ft 表示单位为英尺。

    GEODIST china:city fuzhou guangzhou # 查询两地的直线距离
    GEODIST china:city fuzhou guangzhou km # 自定义单位

  • GEOHASH

    该命令将返回11个字符的Geohash字符串,所以没有精度Geohash,损失相比,使用内部52位表示。返回的geohashes具有以下特性:

    1. 他们可以缩短从右边的字符。它将失去精度,但仍将指向同一地区。

    2. 它可以在 geohash.org 网站使用,网址 http://geohash.org/<geohash-string>。查询例子:Geohash - geohash.org/sqdtr74hyu0.

    3. 与类似的前缀字符串是附近,但相反的是不正确的,这是可能的,用不同的前缀字符串附近。

    GEODIST china:city fuzhou guangzhou # 查询两地的直线距离
    GEODIST china:city fuzhou guangzhou km # 自定义单位

  • GEORADIUS

    以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

    范围可以使用以下其中一个单位:

    • m 表示单位为米。

    • km 表示单位为千米。

    • mi 表示单位为英里。

    • ft 表示单位为英尺。

    GEORADIUS china:city 119 26 500 km # 119 26这个经纬度为中心,方圆500KM的城市
    GEORADIUS china:city 119 26 500 km WITHDIST # 显示到中心的距离
    GEORADIUS china:city 119 26 500 km WITHDIST WITHCOORD # 显示自身的经纬度
    GEORADIUS china:city 119 26 500 km WITHDIST WITHCOORD COUNT 2 # 查询指定数量

  • GEORADIUSBYMEMBER

    这个命令和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点

    指定成员的位置被用作查询的中心。

    GEORADIUSBYMEMBER china:city fuzhou 500 km # 查询福州附近500km的城市

  • 其他使用

    ZRANGE china:city 0 -1 # 查看所有数据
    ZREM china:city fuzhou # 删除

1.2 hyperloglog

HyperLogLog是一种概率数据结构,用于计数唯一的东西(技术上这指的是估计集合的基数)。通常计数唯一项需要使用与您想要计数的项数量成比例的内存,因为您需要记住过去已经见过的元素,以避免多次计数。然而,有一组算法是用内存来换取精度的:在Redis实现的情况下,你以一个标准误差的估计度量结束,这个误差小于1%。这个算法的神奇之处在于,您不再需要使用与计算项数量成比例的内存量,而是可以使用固定数量的内存量!在最坏的情况下是12k字节,或者如果你的HyperLogLog(从现在开始我们就叫它HLL)包含很少的元素,那么就会少得多。

基数

A(1,2,3,4,5)

B(1,2,3,4,5,1)

这个时候 基数 = 5

应用场景

QQ空间访问量(一个人访问十次,还是算一个访问量)

  • 传统模式:使用set保存用户的id,利用set的去重特性,当用户量很大的时候会比较麻烦,而且占用内存比较大
  • Hyperlolos:内存固定,只需要用12KB的内存就可以进行实现(有错误率,但是是在允许范围内的)
PFADD mykey 1 2 3 4 5 # 添加数据
PFCOUNT mykey # 5
PFADD mykey2 1 2 3 1 2 3 1 2 3 # 添加数据
PFCOUNT mykey2 # 3
PFMERGE mykey3 mykey mykey2 # 合并mykey和mykey2为mykey3
PFCOUNT mykey3 # 5
1.3 bitmap

位图不是实际的数据类型,而是在String类型上定义的一组面向位的操作。因为字符串是二进制安全blob,它们的最大长度是512 MB,所以它们适合设置2^32个不同的位。

位图的最大优点之一是,在存储信息时,它们通常可以极大地节省空间。例如,在一个用增量用户id表示不同用户的系统中,仅使用512 MB内存就可以记住40亿用户的单个比特信息(例如,知道用户是否想要接收新闻通讯)。

位存储

主要用于存储用的状态 登录/未登录 打卡/未打卡 两个状态都可以使用Bitmaps

一个用户 365天打卡 = 365bit 差不多46B

SETBIT sign 0 1 # 设置用户sign的第一天 为1
GETBIT sign 0 # 获取第一天是否打卡
BITCOUNT sign # 统计打卡记录,统计为1的总数

2、事务

2.1 什么是Redis事务

一组命令的集合。事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

Redis的单条命令是原子性的,但是事务不保证原子性

所有的命令在事务中,没有执行,只有发起执行命令以后才会执行

2.2 使用流程
2.2.1 开启事务(MULTI)
127.0.0.1:6379> MULTI # 开启事务
OK
2.2.2 命令入队(写命令)
# 命令入队
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
2.2.3 执行事务(EXEC)
127.0.0.1:6379(TX)> EXEC # 执行事务
1) OK
2) OK
3) "v1"
4) OK
2.2.4 放弃事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> get k4
QUEUED
127.0.0.1:6379(TX)> DISCARD # 放弃事务,这个时候事务不会执行
OK
2.2.5 编译型异常

代码写错了,所有命令都不执行

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> set k6 v6
QUEUED
127.0.0.1:6379(TX)> get k5 v5
(error) ERR wrong number of arguments for 'get' command
127.0.0.1:6379(TX)> EXEC # 异常,所有命令都不会执行
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5
(nil)
2.2.6 运行时异常

其他命令正常运行,错误命令抛出错误

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k7 v7
QUEUED
127.0.0.1:6379(TX)> set k8 v8
QUEUED
127.0.0.1:6379(TX)> incr k7
QUEUED
127.0.0.1:6379(TX)> EXEC # 运行的时候异常,其他命令可以正常执行
1) OK
2) OK
3) (error) ERR value is not an integer or out of range
127.0.0.1:6379> get k8
"v8"
127.0.0.1:6379>

3、Jedis

Redis官方推荐的JAVA连接开发工具

3.1 依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>
3.2 使用
@Test
void redisTest01() {
    // 创建Jedis连接对象
    Jedis jedis = new Jedis("127.0.0.1", 6379);
    // 直接调用相关语法即可
    // 检测是否连接成功
    System.out.println(jedis.ping());
    System.out.println(jedis.get("k1"));
    jedis.set("k1", "V1");
    // 关闭连接
    jedis.close();
}
3.3 事务
@Test
void redisTest02() {
    Jedis jedis = new Jedis("127.0.0.1", 6379);
    // 获取一个Redis事务对象
    Transaction multi = jedis.multi();
    try {
        // 往事务中插入语句
        multi.set("k2", "v2");
        multi.set("k3", "v3");
        // 执行事务
        multi.exec();
    } catch (Exception e) {
        e.printStackTrace();
        // 回滚事务
        multi.discard();
    }
    jedis.close();
}

4、 SpringBoot整合Redis

创建的时候选择NoSql的Redis或者手动导入依赖

4.1 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
4.2 配置Redis
spring:
  redis:
    host: 127.0.0.1
    port: 6379
4.3 使用
  • 使用默认配置
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User implements Serializable {
        private String id;
        private String username;
        private String password;
        private String name;
    }
    ​
    @Resource
    RedisTemplate<String, Object> redisTemplate;
    ​
    @Test
    void redisTest03() throws JsonProcessingException {
        // RedisTemplate用来操作不同的数据类型
        ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
        // opsForValue操作字符串
        opsForValue.set("t1", "t1");
        // 操作对象
        User user = new User("1", "admin", "admin", "管理员");
        ObjectMapper mapper = new ObjectMapper();
        // 转成字符串
        opsForValue.set("user:" + user.getId(), mapper.writeValueAsString(user));
        System.out.println(opsForValue.get("user:" + user.getId()));
    }

  • 自己修改(推荐)

    发现存在Redis中的数据长这样 \xac\xed\x00\x05t\x00\x06user:1

    需要自定义一个Template

    添加依赖

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.5</version>
    </dependency>
    @Configuration
    public class RedisConfig {
        // 定义了一个RedisTemplate
        @Bean
        @SuppressWarnings("all")
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            // RedisTemplate 为了自己方便一般直接使用<String,Object>
            RedisTemplate<String, Object> template = new RedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            // 序列化配置
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
            ObjectMapper om = new ObjectMapper();
            // 设置可见度
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            // 启动默认的类型
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            // 序列化类,对象映射设置
            jackson2JsonRedisSerializer.setObjectMapper(om);
            // String的序列化
            StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
            // key采用String的序列化方式
            template.setKeySerializer(stringRedisSerializer);
            // hash的key采用String的学历恶化
            template.setHashKeySerializer(stringRedisSerializer);
            // value采用jackson的序列化
            template.setValueSerializer(jackson2JsonRedisSerializer);
            // hash的value采用jackson的序列化
            template.setHashValueSerializer(jackson2JsonRedisSerializer);
            return template;
        }
    }

  • 工具类
    /**
     * spring redis 工具类
     *
     * @author ruoyi
     **/
    @SuppressWarnings(value = {"unchecked", "rawtypes"})
    @Component
    public class RedisUtil {
        @Autowired
        public RedisTemplate redisTemplate;
    ​
        /**
         * 缓存基本的对象,Integer、String、实体类等
         *
         * @param key   缓存的键值
         * @param value 缓存的值
         */
        public <T> void setCacheObject(final String key, final T value) {
            redisTemplate.opsForValue().set(key, value);
        }
    ​
        /**
         * 缓存基本的对象,Integer、String、实体类等
         *
         * @param key      缓存的键值
         * @param value    缓存的值
         * @param timeout  时间
         * @param timeUnit 时间颗粒度
         */
        public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
            redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
        }
    ​
        /**
         * 设置有效时间
         *
         * @param key     Redis键
         * @param timeout 超时时间
         * @return true=设置成功;false=设置失败
         */
        public boolean expire(final String key, final long timeout) {
            return expire(key, timeout, TimeUnit.SECONDS);
        }
    ​
        /**
         * 设置有效时间
         *
         * @param key     Redis键
         * @param timeout 超时时间
         * @param unit    时间单位
         * @return true=设置成功;false=设置失败
         */
        public boolean expire(final String key, final long timeout, final TimeUnit unit) {
            return redisTemplate.expire(key, timeout, unit);
        }
    ​
        /**
         * 获得缓存的基本对象。
         *
         * @param key 缓存键值
         * @return 缓存键值对应的数据
         */
        public <T> T getCacheObject(final String key) {
            ValueOperations<String, T> operation = redisTemplate.opsForValue();
            return operation.get(key);
        }
    ​
        /**
         * 删除单个对象
         *
         * @param key
         */
        public boolean deleteObject(final String key) {
            return redisTemplate.delete(key);
        }
    ​
        /**
         * 删除集合对象
         *
         * @param collection 多个对象
         * @return
         */
        public long deleteObject(final Collection collection) {
            return redisTemplate.delete(collection);
        }
    ​
        /**
         * 缓存List数据
         *
         * @param key      缓存的键值
         * @param dataList 待缓存的List数据
         * @return 缓存的对象
         */
        public <T> long setCacheList(final String key, final List<T> dataList) {
            Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
            return count == null ? 0 : count;
        }
    ​
        /**
         * 获得缓存的list对象
         *
         * @param key 缓存的键值
         * @return 缓存键值对应的数据
         */
        public <T> List<T> getCacheList(final String key) {
            return redisTemplate.opsForList().range(key, 0, -1);
        }
    ​
        /**
         * 缓存Set
         *
         * @param key     缓存键值
         * @param dataSet 缓存的数据
         * @return 缓存数据的对象
         */
        public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
            BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
            Iterator<T> it = dataSet.iterator();
            while (it.hasNext()) {
                setOperation.add(it.next());
            }
            return setOperation;
        }
    ​
        /**
         * 获得缓存的set
         *
         * @param key
         * @return
         */
        public <T> Set<T> getCacheSet(final String key) {
            return redisTemplate.opsForSet().members(key);
        }
    ​
        /**
         * 缓存Map
         *
         * @param key
         * @param dataMap
         */
        public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
            if (dataMap != null) {
                redisTemplate.opsForHash().putAll(key, dataMap);
            }
        }
    ​
        /**
         * 获得缓存的Map
         *
         * @param key
         * @return
         */
        public <T> Map<String, T> getCacheMap(final String key) {
            return redisTemplate.opsForHash().entries(key);
        }
    ​
        /**
         * 往Hash中存入数据
         *
         * @param key   Redis键
         * @param hKey  Hash键
         * @param value 值
         */
        public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
            redisTemplate.opsForHash().put(key, hKey, value);
        }
    ​
        /**
         * 获取Hash中的数据
         *
         * @param key  Redis键
         * @param hKey Hash键
         * @return Hash中的对象
         */
        public <T> T getCacheMapValue(final String key, final String hKey) {
            HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
            return opsForHash.get(key, hKey);
        }
    ​
        /**
         * 删除Hash中的数据
         *
         * @param key
         * @param hKey
         */
        public void delCacheMapValue(final String key, final String hKey) {
            HashOperations hashOperations = redisTemplate.opsForHash();
            hashOperations.delete(key, hKey);
        }
    ​
        /**
         * 获取多个Hash中的数据
         *
         * @param key   Redis键
         * @param hKeys Hash键集合
         * @return Hash对象集合
         */
        public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
            return redisTemplate.opsForHash().multiGet(key, hKeys);
        }
    ​
        /**
         * 获得缓存的基本对象列表
         *
         * @param pattern 字符串前缀
         * @return 对象列表
         */
        public Collection<String> keys(final String pattern) {
            return redisTemplate.keys(pattern);
        }
    }

  • 使用
    @Autowired
    RedisUtil redisUtil;
    @Test
    void redisTest04() throws JsonProcessingException {
        User user = new User("1", "admin", "admin", "管理员");
        redisUtil.setCacheObject("user:"+user.getId(),user);
        System.out.println(redisUtil.getCacheObject("user:"+user.getId()).toString());
    }

5、小结

本章节中我们学习了三种特殊数据类型的使用、理解了什么是Redis事务、学会使用Redis事务、在JAVA中使用了Redis依赖来操作Redis数据 ,同时使用了SpringBoot和工具类对Redis进行整合。

下一节中我们将会学习Redis的持久化、主从赋值等操作,加深对Redis的使用的理解。


http://www.kler.cn/a/523701.html

相关文章:

  • [C语言日寄] <stdio.h> 头文件功能介绍
  • 三天急速通关JavaWeb基础知识:Day 1 后端基础知识
  • Vscode的AI插件 —— Cline
  • Python3 【函数】:见证算法的优雅与力量
  • OpenHarmony 5.0.2 Release来了!
  • Niagara学习笔记
  • 【452. 用最少数量的箭引爆气球 中等】
  • 使用iis服务器模拟本地资源服务器unityaddressables热更新出错记录
  • C++11中array容器的常见用法
  • fpga系列 HDL:XILINX Vivado Vitis 高层次综合(HLS) 实现 EBAZ板LED控制(上)
  • Unity游戏(Assault空对地打击)开发(3) 摄像机跟随
  • 卡通圣诞节404动画页面模板
  • Spring Security(maven项目) 3.0.2.8版本
  • 17.Word:李楠-学术期刊❗【29】
  • C语言中string.h头文件功能介绍
  • Vscode的AI插件 —— Cline
  • Vue Vine:Vue 组件开发的新范式探索
  • spark3.5.4兼容python 3.10.x以下版本
  • 环境搭建--vscode
  • Object类(2)
  • 使用 KNN 搜索和 CLIP 嵌入构建多模态图像检索系统
  • [论文总结] 深度学习在农业领域应用论文笔记14
  • 人工智能:农业领域的变革力量
  • 如何制作浪漫风格的壁纸
  • 【PowerShell专栏】利用PowerShell开启端口的监听
  • GEE | 提取随机样本点的数据,以CHIRPS降水为例