Redis 的 Java 客户端

一、客户端对比

客户端特点
Jedis以 Redis 命令作为方法名称,学习成本低,简单实用。但是 Jedis 实例是线程不安全的,多线程环境下需要基于连接池来使用
lettuceLettuce 是基于 Netty 实现的,支持同步、异步和响应式编程方式,并且是线程安全的。支持 Redis 的哨兵模式、集群模式和管道模式。
redissonRedission 是一个基于 Redis 实现的分布式、可伸缩的 Java 数据结构集合,包含了诸如 Map、Queue、Lock、Semaphore、AtomicLong 等强大功能。

二、Jedis 入门

1、引入依赖

<dependencies>
    <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 org.example.test;

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

import java.util.Map;

public class JedisTest {

    private Jedis jedis;

    @BeforeEach
    void setUp(){
        // 1、建立连接
        jedis = new Jedis("IP地址", 6379);
        // 2、设置密码
        jedis.auth("密码");
        // 3、选择库
        jedis.select(0);
    }


    @Test
    public void testString(){
        // 存入数据
        String result = jedis.set("name", "张三");
        System.out.println("result = " + result);
        // 获取数据
        String name = jedis.get("name");
        System.out.println(name);
    }

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

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

    @AfterEach
    void closeJedis(){
        if(jedis != null){
            jedis.close();
        }
    }
}

testString 方法测试结果:
在这里插入图片描述
testHash 方法测试结果:
在这里插入图片描述

总结

Jedis 使用的基本步骤:

  • 引入依赖
  • 创建 Jedis 对象,建立连接
  • 使用 Jedis,方法名与 Redis 命令一致
  • 释放资源

Jedis 连接池:

package org.example.test;

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);
        // 设置最长等待时间
        poolConfig.setMaxWaitMillis(1000);
        jedisPool = new JedisPool(poolConfig, "IP地址", 6379, 1000, "密码");
    }

    public static Jedis getJedis(){
        return jedisPool.getResource();
    }
}

三、SpringDataRedis

SpringData 是 Spring 中数据操作的模块,包含对各种数据库的集成,其中对 Redis 的集成模块就叫做 SpringDataRedis。

  • 提供了对不同 Redis 客户端的整合(Lettuce 和 Jedis)
  • 提供了 RedisTemplate 统一 API 来操作 Redis
  • 支持 Redis 的发布订阅模型
  • 支持 Redis 哨兵和 Redis 集群
  • 支持基于 Lettuce 的响应式编程
  • 支持基于 JDK、JSON、字符串、Spring 对象的数据序列化及反序列化
  • 支持基于 Redis 的 JDK Collection 实现

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

API返回值类型说明
redisTemplate.opsForValue()ValueOperations操作 String 类型数据
redisTemplate.opsForHash()HashOperations操作 Hash 类型数据
redisTemplate.opsForList()ListOperations操作 List 类型数据
redisTemplate.opsForSet()SetOperations操作 Set 类型数据
redisTemplate.opsForZSet()ZSetOperations操作 SortedSet 类型数据

3.1 SpringDataRedis 快速入门

1、引入依赖

<!--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>
</dependency>
<!--Jackson 依赖-->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2、配置文件

spring:
  redis:
    host: IP地址
    port: 6379
    password: 密码
    lettuce:
      pool:
        max-active: 8  # 最大连接
        max-idle: 8  # 最大空闲连接
        min-idle: 0  # 最小空闲连接
        max-wait: 100  # 连接等待时间

3、测试代码

package com.example;

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 TestredisApplicationTests {

	// 注入 RedisTemplate
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void testString() {
        // 插入一条 String 类型数据
        redisTemplate.opsForValue().set("name", "王五");
        // 读取一条 String 类型数据
        Object name = redisTemplate.opsForValue().get("name");
        System.out.println(name);
    }

}

4、测试结果
在这里插入图片描述

总结

SpringDataRedis 的使用步骤:

  • 引入 spring-boot-start-data-redis 依赖
  • 在 application.yml 中配置 Redis 信息
  • 注入 RedisTemplate

3.2 SpringDataRedis 的序列化方式

用 Redis 客户端 RDM 来查看一下刚才存入的 name,发现都变成了乱码。这是因为 RedisTemplate 可以接收任意类型的对象,并且帮助我们将其转换成 Redis 可以处理的字节,所以我们存入的键”name“以及值”王五“都被当做成了 Java 对象,而 RedisTemplate 底层对这些对象的处理默认使用的是 JDK 序列化工具 ObjectOutputStream,它可以将一个对象转换成二进制流。
在这里插入图片描述
这种方式有以下的缺点:

  • 可读性差
  • 内存占用较大

3.3 自定义 RedisTemplate 的序列化方式

package com.example.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;
    }
}

测试代码不变,测试结果如下:
在这里插入图片描述

测试存入对象

创建实体类 User:

package com.example.redis.pojo;

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

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

测试方法

@Test
public void testSaveUser(){
    User user = new User("张三", 26);
    redisTemplate.opsForValue().set("user:01", user);
    Object o = redisTemplate.opsForValue().get("user:01");
    System.out.println(o);
}

测试结果:
在这里插入图片描述
在这里插入图片描述

3.4 StringRedisTemplate

尽管 JSON 的序列化方式可以满足我们的需求,但是依然存在一些问题,如图:
在这里插入图片描述
为了在反序列化时知道对象的类型,JSON 序列化器会将类的 class 类型写入 JSON 结果中,存入 Redis,会带来额外的内存开销。
为了节省内存空间,我们并不会使用 JSON 序列化器来处理 value,二是统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化。
Spring 默认提供了一个 StringRedisTemplate 类,它的 key 和 value 的序列化方式默认就是 String 方式。

package com.example.testredis;

import com.example.redis.pojo.User;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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;
import org.springframework.data.redis.core.StringRedisTemplate;

@SpringBootTest
class TestredisApplicationTests {


    private static final ObjectMapper mapper = new ObjectMapper();

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void testStringTemplate() throws JsonProcessingException {
        // 准备对象
        User user = new User("李四", 22);
        // 手动序列化
        String json = mapper.writeValueAsString(user);
        // 写入一条数据到 Redis
        stringRedisTemplate.opsForValue().set("user:200", json);
        // 读取数据
        String value = stringRedisTemplate.opsForValue().get("user:200");
        // 反序列化
        User user1 = mapper.readValue(value, User.class);
        System.out.println("user1 =" + user1);

    }
}

测试结果:
在这里插入图片描述
在这里插入图片描述

3.5 测试存储 hash 类型

测试代码:

@Test
 public void testHash(){
     stringRedisTemplate.opsForHash().put("user:400", "name", "赵六");
     stringRedisTemplate.opsForHash().put("user:400", "age", "22");

     Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400");
     System.out.println("entries : "+entries);
 }

测试结果:
在这里插入图片描述
在这里插入图片描述

3.6 总结

RedisTemplate 的两种序列化实践方案:
方案一:

  • 自定义 RedisTemplate
  • 修改 RedisTemplate 的序列化器为 GenericJacksonJsonRedisSerializer

方案二:

  • 使用 StringRedisTemplate
  • 写入 Redis 时,手动把对象序列化为 JSON
  • 读取 Redis 时,手动把读取到的 JSON 反序列化为对象
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值