Redis:原理速成+项目实战——Redis的Java客户端

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:Redis:原理速成+项目实战——Redis常见命令(数据结构、常见命令总结)
📚订阅专栏:Redis速成
希望文章对你们有所帮助

Redis的Java客户端

  • 客户端对比
  • Jedis快速入门
  • Jedis连接池
  • 认识SpringDataRedis
  • RedisTemplate快速入门
  • RedisTemplate的RedisSerializer
  • StringRedisTemplate

客户端对比

官方给Java推荐了几个客户端:

客户端优点
Jedis以Redis命令作为方法名称,学习成本低,简单实用。但Jedis实例的线程不安全,多线程环境需要基于连接池来使用
Lettuce(Spring官方默认兼容)Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且线程安全。支持Redis的哨兵模式、集群模式和管道模式
RedissonRedisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合

我们主要要掌握前两种,Spring的整合能力强,而SpringDataRedis正好整合了Jedis与Lettuce。

Jedis快速入门

Jedis的官网地址:Jedis官网地址
我们先进行一个快速的入门,我们需要进行一个Jedis的单元测试的实验:
1、创建一个maven工程并且引入Jedis依赖以及单元测试依赖:

	<dependencies>
       <!--Jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.7.0</version>
        </dependency>
        <!--单元测试-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
	</dependencies>

2、建立测试类,直接上代码:

package com.wang.test;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import redis.clients.jedis.Jedis;

public class JedisTest {
    private Jedis jedis;

    @BeforeEach
    void setUp(){
        //建立连接,需要传入IP地址和端口号
        jedis = new Jedis("192.168.177.130", 6379);
        //设置密码
        jedis.auth("123456");
        //选择库
        jedis.select(0);
    }

    @Test
    void testString(){
        //插入数据,方法名称即为Redis命令名称,比较方便简洁
        String result = jedis.set("name", "布布");
        System.out.println("result = " + result);
        //获取数据
        String name = jedis.get("name");
        System.out.println("name = " + name);
    }

    @Test
    void testHash(){
        //插入哈希数据
        jedis.hset("user:1", "name", "Jack");
        jedis.hset("user:1", "age", "21");

        //获取
        Map<String, String> map = jedis.hgetAll("user:1");
        System.out.println(map);
    }

    @AfterEach
    void tearDown(){
        //释放资源
        if (jedis != null){
            jedis.close();
        }
    }
}

过程总结:
1、引入依赖
2、创建Jedis对象,建立连接
3、使用Jedis,方法名与Redis命令一致
4、释放资源

Jedis连接池

Jedis本身线程不安全,且频繁的创建和释放连接会造成性能上的损耗,所以需要使用Jedis的连接池来代替Jedis的直连方式。
官方也提供了这样的一个Jedis连接池的工厂类,这个工厂类的主要作用是设置池的一些配置(最大连接数,最大、最小空闲连接等),并且设置了一个静态get方法,当我们需要一个Jedis对象的时候,就可以在这个类中直接调用这个方法了:

package com.wang.jedis.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisConnectionFactory {
    private static final JedisPool jedisPool;

    static {
        //配置连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        //最大连接数
        poolConfig.setMaxTotal(8);
        //最大空闲连接
        poolConfig.setMaxIdle(8);
        //最小空闲连接
        poolConfig.setMinIdle(0);
        //设置最长等待时间(ms)
        poolConfig.setMaxWaitMillis(1000);
        //创建连接池对象
        jedisPool = new JedisPool(poolConfig,
                "192.168.177.130", 6379, 1000, "123456");
    }
    //获取Jedis对象
    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}

以后当我们需要创建Jedis对象的时候就不需要再new了,直接用

jedis = JedisConnectionFactory.getJedis();

即可。

认识SpringDataRedis

SpringData是Spring中数据操作的模块,包含了对各种数据库的集成,其中SpringDataRedis就是对Redis的集成模块,其官网地址:
SpringDataRedis官网地址
其具备的一些特征:
1、提供了对不同Redis客户端的整合(Jedis与Lettuce)
2、提供了RedisTemplate统一API来操作Redis
3、支持Redis的发布订阅模型
4、支持Redis哨兵和Redis集群
5、支持基于Luttuce的响应式编程
6、支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化
7、支持基于Redis的JDKCollection实现

SpringDataRedis提供了RedisTemplate工具类,封装了各种对Redis多的操作,并且将不同数据类型的操作API封装到了不同的类型中:

API说明
redisTemplate.opsForValue()操作String类型数据
redisTemplate.opsForHash()操作Hash类型数据
redisTemplate.opsForList()操作List类型数据
redisTemplate.opsForSet()操作Set类型数据
redisTemplate.opsForZset()操作SortedSet类型数据
redisTemplate通用命令

RedisTemplate快速入门

SpringBoot已经提供了对SpringDataRedis的支持,使用很方便,这里进行一些实验:
1、新建SpringBoot类型工程,并引入依赖:

		<!--Redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--连接池依赖-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <!--如果报错,就不要指定版本-->
            <version>2.10.0</version>
        </dependency>

2、配置Redis信息:

spring:
  data:
    redis:
      host: 192.168.177.130
      port: 6379
      password: 123456
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0
          max-wait: 1000ms

3、注入RedisTemplate:

@Autowired
private RedisTemplate redisTemplate;

4、编写测试类代码:

@Test
void testString() {
	//写入一个String数据
    redisTemplate.opsForValue().set("name", "布布");
    //获取String数据
    Object name = redisTemplate.opsForValue().get("name");
    System.out.println("name = " + name);
}

RedisTemplate的RedisSerializer

上述的代码运行完毕以后,我们回到命令行去查看:
在这里插入图片描述
这时候我们可以发现,前面插入的键值对在这里完全乱码了。
这是RedisTemplate的序列化的问题,原理自行去理解,重点是我们不能再用这种序列化了。
因此,我们可以自定义RedisTemplate的序列化方式,并且bean方式注入,代码如下:

package com.wang.redis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory){
        //创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //设置连接工厂
        template.setConnectionFactory(connectionFactory);
        //创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        //设置Key的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        //设置value的序列化
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        //返回
        return template;
    }

}

以上代码需要引入JackSon依赖:

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.14.2</version>
        </dependency>

之后就可以修改一下我们的测试类的template使用方式了:

package com.wang;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;

@SpringBootTest
class RedisDemoApplicationTests {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    void testString() {
        //写入一个String数据
        redisTemplate.opsForValue().set("name", "布布");
        //获取String数据
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
    }
}

现在已经正常序列化了:
在这里插入图片描述
我们再做个实验,来看看对于一个对象,序列化是如何进行的,转化的json又是怎样的:
1、我们创建一个User类:

package com.wang.redis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private Integer age;
}

2、编写测试类:

@Test
void testSaveUser(){
	//写入数据
    redisTemplate.opsForValue().set("user:100", new User("布布", 21));
    //获取数据
    User user = (User) redisTemplate.opsForValue().get("user:100");
    System.out.println("user = " + user);
}

运行结果:
在这里插入图片描述

StringRedisTemplate

尽管json的序列化方式可以满足我们的需求,但仍然存在一些问题,为了在反序列化时知道对象的类型,json序列化器会将类的class写入json,存入Redis,会造成内存额外开销。
而StringRedisTemplate可以解决这个问题,编写代码测试:

private static final ObjectMapper mapper = new ObjectMapper();

@Test
void testSaveUser() throws JsonProcessingException {
	//创建对象
    User user = new User("布布", 21);
    //手动序列化
    String json = mapper.writeValueAsString(user);
    //写入数据
    stringRedisTemplate.opsForValue().set("user:200", json);
    //获取数据
    String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
    //手动反序列化
    User user1 = mapper.readValue(jsonUser, User.class);
    System.out.println("user1 = " + user1);
}

在这里插入图片描述
在这里将RedisTemplate的两种序列化方案总结:
方案一(简单但占空间):
1、自定义RedisTemplate
2、修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
方案二(省内存但复杂):
1、使用StringRedisTemplate
2、写入Redis时手动把对象序列化为JSON
3、读取Redis时,手动把读取JSON反序列化为对象

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/280465.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt Creator可视化交互界面exe快速入门5

上一期介绍了加法计算器,本期介绍QObject定时器。 首先一样先建个工程,比如我这项目名为QObject 本期的任务就是制作图片在界面上显示,然后每秒定时切换,点击另一个暂停按钮,可以定格当前图片,即取消定时切换功能。 显示图片的我们可以使用显示里面的label 这个用于显示…

STM32+Codesys工业软件PLC解决方案

工业控制系统在现代制造和自动化领域扮演着关键角色, 基于IEC 61131-3 标准的控制器编程开发软件平台CODESYS&#xff0c;适用于多种行业的控制系统的开发,使用户方便快捷地对自动化工程进行编程和配置&#xff0c;完成项目开发、软件测试和应用调试。 本次STM32联合合作伙伴C…

设计模式(4)--对象行为(8)--状态

1. 意图 允许一个对象在其内部状态改变时改变它的行为。 2. 三种角色 上下文环境(Context)、抽象状态(State)、具体状态(Concrete State) 3. 优点 3.1 将与特定状态相关的行为局部化&#xff0c;并且将不同状态的行为分割开来。 3.2 使得状态转换显式化。 3.3 State对象可被共…

超详细YOLOv8姿态检测全程概述:环境、训练、验证与预测详解

目录 yolov8导航 YOLOv8&#xff08;附带各种任务详细说明链接&#xff09; 搭建环境说明 不同版本模型性能对比 不同版本对比 参数解释 模型解释 训练 训练示意代码 训练数据与.yaml配置方法 .yaml配置 数据集路径 标签数据说明 训练参数说明 训练过程示意及输出…

低代码开发中业务蓝图的重要性:业务需求与系统实现的桥梁

在低代码应用开发领域&#xff0c;业务蓝图是一个至关重要的工具&#xff0c;它提供了组织业务流程需求的详细信息。它类似于一份指导开发人员进行应用开发的路线图&#xff0c;确保与业务的战略目标和需求保持一致。 低代码方法学&#xff0c;顾名思义&#xff0c;即减少了传…

基于AM62x的ARM+FPGA+Codesys低成本软PLC解决方案

GPMC并口简介 GPMC(General Purpose Memory Controller)是TI处理器特有的通用存储器控制器接口&#xff0c;支持8/16bit数据位宽&#xff0c;支持128MB访问空间&#xff0c;最高时钟速率133MHz。GPMC是AM62x、AM64x、AM437x、AM335x、AM57x等处理器专用于与外部存储器设备的接口…

交叉验证的种类和原理(sklearn.model_selection import *)

交叉验证的种类和原理 所有的来自https://scikit-learn.org/stable/modules/cross_validation.html#cross-validation-iterators并掺杂了自己的理解。 文章目录 前言一、基础知识1.1 交叉验证图形表示1.2 交叉验证主要类别 二、部分交叉验证函数&#xff08;每类一个&#xff0…

SQL注入【ByPass有点难的靶场实战】(九)

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 0、总体思路 先确认是否可以SQL注入&#xff0c;使用单…

Unity坦克大战开发全流程——开始场景——设置界面

开始场景——设置界面 step1&#xff1a;设置面板的背景图 照着这个来设置就行了 step2&#xff1a;写代码 关联的按钮控件 监听事件函数 注意&#xff1a;要在start函数中再写一行HideMe函数&#xff0c;以便该面板能在一开始就能隐藏自己。 再在BeginPanel脚本中调用该函数即…

ESP32入门六(读取引脚的模拟信号[2]:信号出现误差的原因)

在之前的章节中&#xff0c;我们测试了用ESP32来接收模拟电压信号&#xff0c;在测试中&#xff0c;读取到的数据与现实存在一定的误差&#xff0c;在这一篇中&#xff0c;我们尝试了解出现误差的原因和解决方法。 对于出现的误差&#xff0c;有多种软件和硬件方面的原因: 一、…

红黑树的删除

导航链接 红黑树的性质     红黑树的旋转、变色     红黑树的插入     红黑树的删除 文章目录 导航链接二叉搜索树如何删除结点&#xff1f;场景一&#xff1a;删除没有孩子的结点场景二&#xff1a;删除有一个孩子的结点场景三&#xff1a;删除有两个孩子的结点 红…

海康visionmaster-分支字符:控制调试模式开关的方

在图的右边分支字符模块有两个分支&#xff0c;通过 C#代码 GetParamValue 函数可以看到调试模 式的相关参数 ModuleInfoList 的值为&#xff1a;4#1#0KaTeX parse error: Expected EOF, got # at position 3: 10#̲0#0。其中分支 4#1#0$的 4 表示模 块 id&#xff0c;1 表示这…

操作系统:可变分区管理

有作业序列&#xff1a;作业A要求42K&#xff1b;作业B要求27K&#xff0c;作业C要求22K&#xff0c;作业和空闲内存区如下图所示&#xff0c;请画出最佳适应算法空闲队列图&#xff0c;并分析最佳适应算法是否适合该作业系列。 答&#xff1a;最佳适应算法是按照空闲块由小到大…

Harmony全局应用生命周期 EntryAbility.ts 讲解

之前 我们说过 page页面的生命周期 组件的生命周期 其实他和uni一样有一个整个应用的生命周期 我们如下图打开EntryAbility.ts 这是我们整个程序app的状态控制 他这里也有几个全局的生命周期 比如 我们手机 点开当前 App 启动 app 会触发 它的 onCreate 生命周期 当我们从手…

前端 js 基础(2)

js For In for in 循环遍历 person 对象每次迭代返回一个键 (x)键用于访问键的值键的值为 person[x] 如果索引顺序很重要&#xff0c;请不要在数组上使用 for in。 索引顺序依赖于实现&#xff0c;可能不会按照您期望的顺序访问数组值。 当顺序很重要时&#xff0c;最好使用 f…

元旦特辑:Note6---选择排序

目录 前言❌ 1. 基本思想⚠️ 2. 直接选择排序&#x1f7e2; 2.1 思路分析✳️ 2.2 代码实现❎ 2.2.1 sort.h 2.2.2 sort.c 2.2.3 test.c 2.3 问题解决❇️ 2.3.1 sort.c修改 2.4 特性总结✅ 3. 堆排序&#x1f535; 3.1 代码实现&#x1f3e7; 3.2 特性总结&…

Centos安装Kafka(KRaft模式)

1. KRaft引入 Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者在网站中的所有动作流数据。其核心组件包含Producer、Broker、Consumer&#xff0c;以及依赖的Zookeeper集群。其中Zookeeper集群是Kafka用来负责集群元数据的管理、控制器的选举等。 由…

c++ 简单实用万能异常捕获

多层捕获异常&#xff0c;逐渐严格。并打印出错信息和位置&#xff1a;哪个文件&#xff0c;哪个函数&#xff0c;具体哪一行代码。 #include <stdexcept> // 包含标准异常类的头文件try {int a 2 / 0; }catch (const std::runtime_error& e) {// 捕获 std::runt…

浅谈 JSON 对象和 FormData 相互转换,打通前端与后端的通信血脉

前言 大家都知道&#xff0c;前端在和后台进行交互联调时&#xff0c;肯定避免不了要传递参数&#xff0c;一般情况下&#xff0c;params 在 get 请求中使用&#xff0c;而 post 请求下&#xff0c;我们有两种常见的传参方式&#xff1a; JSON 对象格式和 formData 格式&#x…

AtCoder Beginner Contest 334 G

G.Christmas Color Grid 2&#xff08;枚举&#xff0c;Tarjan&#xff09; 题意&#xff1a; 本题与问题 E E E类似。有一个 H H H行和 W W W列的网格&#xff0c;每个单元格都被涂成红色或绿色。用 ( i , j ) (i,j) (i,j)表示从上到下第 i i i行、从左到右第 j j j列的单元…