Redis的Java客户端

Redis 的 Java 客户端

  • Jedis
  • 优点:以 Redis 命令作为方法名称,学习成本低廉,简单且实用
  • 缺点:Jedis 的实例是线程不安全的,在多线程的环境下需要基于线程池来使用
  • lettuce(spring 官方默认)
  • 基于 Netty 实现的,支持同步、异步和响应式编程方式,并且是线程安全的。支持 Redis 的哨兵模式、集群模式、管道模式
  • Redisson(适用于分布式的环境)
  • 基于 Redis 实现的分布式、可伸缩的 Java 数据结构的集合。包含 Map、Queue、Lock、Semaphore、AtomicLong等强大的功能

Jedis

Jedis 基本使用步骤

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

测试 Jedis 相关方法

如果 @BeforeEach 报错,记得在 pom 文件里面引入 Junit API 包的依赖


    org.junit.jupiter
    junit-jupiter-api
    5.8.2
    test

(这里以 String 和 Hash 两种类型为例)

package com.lcha.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("xxxxxxxxxx",6379);
        //2.设置密码
        jedis.auth("xxxxxxxxx");
        //3.选择库
        jedis.select(0);
    }

    @Test
    void testStr(){
        //4.存入数据
        String result = jedis.set("name", "胡歌");
        System.out.println("result = " + result);
        //5.获取数据
        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 map = jedis.hgetAll("user:1");
        System.out.println(map);
    }

    @AfterEach
    void tearDown(){
        //6.释放连接
        if(jedis != null){
            jedis.close();
        }
    }
}

Jedis连接池

Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。

首先创建一个 Jedis 连接池工具类

package com.lcha.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);  //最大连接数:8
        poolConfig.setMaxIdle(8);   //最大空闲连接
        poolConfig.setMinIdle(0);
        poolConfig.setMaxWaitMillis(1000);
        //创建连接池对象
        jedisPool = new JedisPool(poolConfig,"xxxx",6379,
                1000,"xxxx");
    }

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

更改之前 Jedis 的连接方式,采用连接池连接的方式

package com.lcha.test;

import com.lcha.jedis.util.JedisConnectionFactory;
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("xxxx",6379);
        jedis = JedisConnectionFactory.getJedis();
        //2.设置密码
        jedis.auth("xxxx");
        //3.选择库
        jedis.select(0);
    }

    @Test
    void testStr(){
        //4.存入数据
        String result = jedis.set("name", "胡歌");
        System.out.println("result = " + result);
        //5.获取数据
        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 map = jedis.hgetAll("user:1");
        System.out.println(map);
    }

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

注意:当使用连接池连接时,代码最后的 if(jedis != null){jedis.close();} 不会真正的销毁连接,而是将本连接归还到连接池中

源码如下:

public void close() {
        if (this.dataSource != null) {
            Pool pool = this.dataSource;
            this.dataSource = null;
            if (this.isBroken()) {
                pool.returnBrokenResource(this);
            } else {
                pool.returnResource(this); //注意这里!!!!
            }
        } else {
            this.connection.close();
        }

    }

SpringDataRedis

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

官网地址:https://spring.io/projects/spring-data-redis

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

RedisTemplate 工具类

API 返回值类型 说明 RedisTemplate.opsForValue() ValueOperations 操作 String 类型数据 RedisTemplate.opsForHash() HashOperations 操作 Hash 类型数据 RedisTemplate.opsForList() ListOperations 操作 List 类型数据 RedisTemplate.opsForSet() SetOperations 操作 Set 类型数据 RedisTemplate.opsForZSet() ZSetOperations 操作 SortedSort 类型数据 RedisTemplate 通用命令

使用步骤

  1. 引入 spring-boot-starter-data-redis 依赖

            org.springframework.boot
            spring-boot-starter-data-redis

            org.apache.commons
            commons-pool2

  1. 在 application.yml 文件中配置 Redis 信息
spring:
  redis:
    host: xxxx
    port: 6379
    password: xxxx
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 100ms
  1. 注入 RedisTemplate 并使用
package com.lcha;

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 redisTemplate;

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

}

序列化问题

RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:

Redis的Java客户端

缺点:

  1. 可读性差
  2. 内存占用较大

解决方法:改变序列化器

自定义 RedisTemplate 序列化方式

package com.lcha.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;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
        //创建 RedisTemplate 对象
        RedisTemplate 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;
    }
}

重新运行刚才的代码,结果如下图所示:

Redis的Java客户端

存储对象数据时也是一样的

  1. 创建一个对象类
package com.lcha.redis.pojo;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String name;
    private Integer age;
}
  1. 编写测试方法
    3.
    @Test
    void testSaveUser(){
        redisTemplate.opsForValue().set("user:100", new User("胡歌",21));
        User o = (User) redisTemplate.opsForValue().get("user:100");
        System.out.println("o = " + o);
    }

Redis的Java客户端

JSON方式依然存在的缺陷

尽管 JSON 的序列化方式可以满足我们的需求,但是依然存在一些问题。

Redis的Java客户端

为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。

如何解决

为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。

  1. 直接使用 StringRedisTemplate 即可
package com.lcha;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lcha.redis.pojo.User;
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 RedisStringTests {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void testString() {
        //写入一条String数据
        stringRedisTemplate.opsForValue().set("name", "胡歌");
        //获取string数据
        Object name = stringRedisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
    }

    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);
    }

}

Redis的Java客户端

对 Hash 类型的操作

  1. 编写方法
@Test
    void testHash(){
        stringRedisTemplate.opsForHash().put("user:300", "name", "张三");
        stringRedisTemplate.opsForHash().put("user:300", "age", "18");

        Map entries = stringRedisTemplate.opsForHash().entries("user:300");
        System.out.println("entries = " + entries);

    }

Redis的Java客户端

Original: https://www.cnblogs.com/lcha-coding/p/16327868.html
Author: 染沁
Title: Redis的Java客户端

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/621404/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • mysql练习题emp,dept

    DROP DATABASE IF EXISTS emp; CREATE DATABASE emp; USE emp; &#xA0;CREATE TABLE dept( &a…

    数据库 2023年6月9日
    0168
  • 面试题: 字符串转整型 终结者

    随着代码手感增强, 想为这个问题写个终结者系列. 缅怀下曾经的自己. 我们审视下这个问题, 整数字符串转成整数. 那么意味着有效字符仅有 “+-0123456789&#…

    数据库 2023年6月9日
    085
  • Linux 下安装 redis

    2、使用命令下载: 3、将文件拷贝到安装目录 /usr/local 目录下 4、进入 /usr/local 目录下,解压安装包 5、进入解压后的目录 6、编译,将 redis 安装…

    数据库 2023年6月14日
    082
  • Spring(二)-生命周期 + 自动装配(xml) +自动装配(注解)

    1、生命周期 Spring容器的 bean的生命周期; Truck @Data @ToString public class Truck { //品牌 private String…

    数据库 2023年6月16日
    091
  • 手写LRU缓存淘汰算法

    概述 LRU算法全称为 Least Recently Used是一种常见的页面缓存淘汰算法,当缓存空间达到达到预设空间的情况下会删除那些 最久没有被使用的数据 。 常见的页面缓存淘…

    数据库 2023年6月11日
    0119
  • Spring boot 项目配置 Maven 资源文件分离打包

    需要引入三个Maven插件: maven-jar-plugin:用于打包代码,并去除不需要一起打包的resource文件 maven-assembly-plugin:主要用来打压缩…

    数据库 2023年6月9日
    063
  • 数据火器库八卦系列之瑞士军刀随APP携带的SQLite

    来源:云数据库技术 数据库打工仔喃喃自语的八卦历史 为导弹巡洋舰设计,用在手机上的数据库 Small and Simple, and Better 如何看出是自己的娃:产品定位,特…

    数据库 2023年6月11日
    096
  • 多线程

    多线程使用Callable实现多线程 多线程第三种创建方式 定义一个任务类,实现Callable接口,结合FutureTask完成 交给Thread处理,重写call方法 目标:学…

    数据库 2023年6月16日
    089
  • 视野 | OpenSearch,云厂商的新选择?

    王奇 顾问软件工程师目前从事 PaaS 中间件服务(Redis / MongoDB / ELK 等)开发工作,对 NoSQL 数据库有深入的研究以及丰富的二次开发经验,热衷对 No…

    数据库 2023年5月24日
    091
  • vue2框架基础

    一、什么是vue? vue是一个优秀的前端框架,他与Angular.js、React.js成为前端三大主流框架。他是一套构建用户界面的框架,只关注视图层,可以完成大型项目的开发,框…

    数据库 2023年6月14日
    082
  • 2022-08-17 DQL—-子查询,日期格式

    子查询、日期格式 DQL查询语言 子查询 根据结果集中的行数,子查询可以分为以下几类: [En] According to the number of rows in the re…

    数据库 2023年5月24日
    084
  • 数据库原理一—MySQL基本架构与索引

    MySQL基本架构 Server层包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现。存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持In…

    数据库 2023年6月6日
    071
  • 03-MySQL事务

    数据库事务 1、事务特性 1.1、原子性 即不可分割性,事务要么全部被执行,要么就全部不被执行 1.2、一致性 事务的执行使得数据库从一种正确状态转换成另一种正确状态 1.3、隔离…

    数据库 2023年6月16日
    094
  • docker 搭建php 开发环境 添加扩展redis、swoole、xdebug

    docker-compose搭建lnmp 先决条件 首先需要安装docker 安装docker-compost 1、创建lnmp工作目录 #创建三个目录 mkdir lnmp &a…

    数据库 2023年6月11日
    076
  • synchronized 是可重入锁吗?为什么?

    什么是可重入锁? 若一个程序或子程序可以”在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(reentr…

    数据库 2023年6月16日
    085
  • day44-反射03

    Java反射03 3.通过反射获取类的结构信息 3.1java.lang.Class类 getName:获取全类名 getSimpleName:获取简单类名 getFields:获…

    数据库 2023年6月11日
    064
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球