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

spring boot整合常用redis客户端(Jedis、Lettuce、RedisTemplate、Redisson)常见场景解决方案

Java操作redis有三种客户端供选择:Jedis、Lettuce、Redisson。
在实际项目中运用最多的客户端还是Redisson、RedisTemplate;其中RedisTemplate并非是一个新的redis客户端实现,RedisTemplate是Spring Data Redis中提供的封装好的redis操作模板,Spring Data Redis则是Spring Data下的一个子模块(Spring Data还包括jdbc、jpa、elasticsearch、redis等等数据库访问实现),RedisTemplate是对Jedis和Lettuce的进一步封装,简化Jedis和Lettuce对redis的操作,在spring boot2.x之后的版本使用RedisTemplate时默认采用Lettuce实现,spring boot1.x的版本默认是采用Jedis实现。

redis客户端中命令行获取value时出现中文乱码,可用以下命令(在redis客户端外面执行该命令):

redis-cli --raw get redisTemplate
redis-cli:redis客户端可执行文件
redisTemplate:获取value的key

一、Jedis

Jedis 是一款老牌 Redis 的 Java 客户端。
优点:
1、Jedis 的 API 提供了比较全面的 Redis 命令的支持
2、Jedis 中的 Java 方法基本和 Redis 的 API 保持着一致,也就是说了解 Redis 的API,可以熟练的使用 Jedis
3、支持 pipelining、事务、LUA Scripting、Redis Sentinel、Redis Cluster等等 redis 提供的高级特性
4、客户端轻量,简洁,便于集成和改造
5、使用广泛,开发人员易上手
缺点:
1、使用阻塞的 I/O 操作,且其方法调用都是同步的,程序流需要等到 sockets 处理完 I/O 才能执行,不支持异步
2、Jedis 在实现上是直接连接的 redis server,如果在多线程环境下是非线程安全的,这个时候可以使用连接池来管理 Jedis,解决 Jedis 客户端实例存在非线程安全的问题(也就是可以通过配置JedisPool来实现基于Jedis的连接池)
3、不支持读写分离,需要自己实现
4、技术文档差,可以说几乎没有
通过配置 JedisPool 设置连接池并将JedisPool对象注入到spring容器内,使用时通过 @Autowired 方式注入JedisPool 使用。
jar包:

        <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version></dependency>

配置信息:

spring:redis:host: 127.0.0.1   #redis服务端主机ipport: 6379   #redis端口号#password: 123456   #redis密码database: 0   #使用redis的哪个库,redis有16个库,默认是0即第一个库client-name: ceshi    #生成的客户端实例名称(jedis连接的名称)connection-timeout: 3000   #连接redis超时时间so-timeout: 2000   #socket超时时间max-total: 16    #连接池最大连接数max-idle: 8   #连接池最大空闲连接数min-idle: 4   #连接池最小空闲连接数max-wait-millis: 1000   #从连接池获取连接最长等待时间test-on-borrow: true   #在连接池分配Jedis实例时,是否测试连接可用性

JedisPool配置类:

package com.database.pool.testpool.config;import lombok.Data;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;/*** JedisPool连接池在创建时不会建立连接,只有客户端使用JedisPool获取连接时才会检查是否有空闲的连接,如果没有则创建连接给客户端,如果有则直接分配空闲连接给客户端*/
@Data
@Configuration
@ConfigurationProperties("spring.redis")//获取spring.redis开头的配置赋值给对应字段
public class JedisPoolConfig {/** 以下参数是针对创建redis连接的设置 **//*** redis服务端IP*/private String host;/*** redis端口*/private Integer port;/*** 连接超时时间*/private Integer connectionTimeout;/*** socket超时时间*/private Integer soTimeout;/*** redis密码*/private String password;/*** 使用哪个数据库*/private Integer database;/*** 生成的客户端实例名称*/private String clientName;/**  以下参数是针对连接池jedisPool的设置  **//*** 连接池最大连接数*/private Integer maxTotal;/*** 连接池最大空闲连接数*/private Integer maxIdle;/*** 连接池最小空闲连接数*/private Integer minIdle;/*** 当连接池中的jedis 实例都被分配完时,是否要进行阻塞*/private boolean blockWhenExhausted;/*** 从连接池获取连接最长等待时间,当blockWhenExhausted为true时,最大的阻塞时长*/private Integer maxWaitMillis;/*** 创建Jedis实例时,是否进行连接可用性测试,默认关闭,如果打开,则保证创建的都是连接可用的Jedis实例*/private boolean testOnCreate;/*** 在连接池分配Jedis实例时,是否测试连接可用性,默认关闭,如果打开,则保证分配出去的连接都是可用的*/private boolean testOnBorrow;/*** 归还Jedis实例到连接池时,是否测试连接可用性,默认关闭*/private boolean testOnReturn;/*** 是否开启一个idle object evitor线程对空闲的Jedis实例进行扫描,如果验证失败,此Jedis实例会被从资源池中删除掉,只有在timeBetweenEvictionRunsMillis大于0时才生效*/private boolean testWhileIdle;/*** idle object evitor线程两次扫描之间要sleep的毫秒数即扫描空闲连接是否可用的时间间隔*/private Integer timeBetweenEvictionRunsMillis;/*** idle object evitor线程每次扫描的最多的对象数*/private Integer numTestsPerEvictionRun;/*** 空闲驱逐时间,一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才生效*/private Integer minEvictableIdleTimeMillis;/*** 软空闲驱逐时间,在minEvictableIdleTimeMillis基础上,还需要检测至少softMinEvictableIdleTimeMillis长时间,才会进行驱逐*/private Integer softMinEvictableIdleTimeMillis;/*** 分配连接资源时,是否使用LIFO(last in first out)策略,即分配最新使用的连接;还是使用FIFO策略,分配最久没有使用的连接;默认为true,即LIFO*/private boolean lifo;/*** 驱逐连接池中连接的策略,默认实现逻辑:资源的空闲毫秒数,如果大于空闲驱逐时间minEvictableIdleTimeMillis,或大于softMinEvictableIdleTimeMillis且当前的空闲资源数量大于配置的最小空闲资源数量,则进行驱逐,可重写该接口自定义驱逐策略*///private EvictionPolicy evictionPolicy;/*** JedisPool针对单节点的redis服务使用jedis连接池时做配置* JedisPool和JedisSentinelPool使用完jedis连接后都需要调用close方法而JedisCluster使用完jedis连接后不需要调用close方法(内部机制会自动进行连接状态转换)* @return*/@Beanpublic JedisPool generateJedisPoolFactory() {//JedisSentinelPool  Sentinel模式的redis服务需要配置JedisSentinelPool而非JedisPool//JedisCluster    集群模式的redis服务需要配置JedisCluster而非JedisPoolGenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();poolConfig.setMaxTotal(maxTotal);poolConfig.setMaxIdle(maxIdle);poolConfig.setMinIdle(minIdle);poolConfig.setMaxWaitMillis(maxWaitMillis);poolConfig.setTestOnBorrow(testOnBorrow);JedisPool jedisPool = new JedisPool(poolConfig, host, port,connectionTimeout,soTimeout,password,database,clientName);return jedisPool;}}

使用JedisPool类:

package com.database.pool.testpool.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;@RequestMapping("/test")
@RestController
public class TestController {@Autowiredprivate JedisPool jedisPool;/*** jedis 测试* @param value* @return*/@GetMapping("/info/{value}")public String info(@PathVariable("value") String value){Jedis resource = null;String jedis = null;try {//jedisPool.getNumActive()   活跃连接数(被客户端使用的连接数)//jedisPool.getNumIdle()  空闲连接数(未被客户端使用可用于分配给客户端使用的连接数)System.out.println("jedisPool信息:"+ jedisPool.getNumActive()+"  "+jedisPool.getNumWaiters()+"  "+jedisPool.getNumIdle());resource = jedisPool.getResource();//获取连接System.out.println("jedisPool信息:"+ jedisPool.getNumActive()+"  "+jedisPool.getNumWaiters()+"  "+jedisPool.getNumIdle()+"  "+resource.clientGetname());resource.set("jedis",value);//使用连接jedis = resource.get("jedis");Jedis resource1 = jedisPool.getResource();System.out.println("jedisPool信息:"+ jedisPool.getNumActive()+"  "+jedisPool.getNumWaiters()+"  "+jedisPool.getNumIdle());resource1.close();System.out.println("jedisPool信息:"+ jedisPool.getNumActive()+"  "+jedisPool.getNumWaiters()+"  "+jedisPool.getNumIdle());}finally {//使用完jedis之后需要close,这个close的作用不是把jedis连接关闭,而是将这个jedis连接从活跃状态(被客户端使用的状态)设置为空闲状态,方便下次通过getResource//获取该连接给其他客户端使用,否则下次getResource时由于没有空闲连接会再次创建一个jedis连接,不能达到复用连接的目的if (resource != null)resource.close();//关闭连接(将连接设置为空闲状态)System.out.println("jedisPool信息:"+ jedisPool.getNumActive()+"  "+jedisPool.getNumWaiters()+"  "+jedisPool.getNumIdle());Jedis resource2 = jedisPool.getResource();System.out.println("jedisPool信息:"+ jedisPool.getNumActive()+"  "+jedisPool.getNumWaiters()+"  "+jedisPool.getNumIdle());resource2.close();System.out.println("jedisPool信息:"+ jedisPool.getNumActive()+"  "+jedisPool.getNumWaiters()+"  "+jedisPool.getNumIdle());}return jedis;}}

测试:
在这里插入图片描述
在这里插入图片描述
哨兵模式配置:
在这里插入图片描述
集群模式配置:
在这里插入图片描述

二、Lettuce

Lettuce 是一种可扩展的、线程安全的 Redis 高级客户端,从 Spring Boot 2.x 开始, Lettuce 已取代 Jedis 成为SpringBoot 默认的 Redis 客户端
优点:
1、相比于 Jedis,Lettuce 属于后起之秀,对 Redis 支持更加全面,并且解决了 Jedis 客户端实例存在非线程安全的问题
2、支持同步编程,异步编程,响应式编程,自动重新连接,主从模式,集群模块,哨兵模式,管道和编码器等等高级的 Redis 特性
3、Lettuce 底层基于 Netty 框架的事件驱动与 redis 通信,采用了非阻塞的 I/O 操作,可异步调用,相比 Jedis,性能高
4、Lettuce 的 API 是线程安全的,如果不是执行阻塞和事务操作,如 BLPOP 和MULTI/EXEC 等命令,多个线程就可以共享一个连接,性能方面差异很小
缺点:
1、API 更加抽象,学习使用成本高
jar包:

        <dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.1.8.RELEASE</version></dependency>

配置信息:

spring:redis:host: 127.0.0.1   #redis服务端主机ipport: 6379   #redis端口号#password: 123456   #redis密码database: 0   #使用redis的哪个库,redis有16个库,默认是0即第一个库client-name: ceshi    #生成的客户端实例名称(lettuce连接的名称)connection-timeout: 3000   #连接redis超时时间

配置类:

package com.database.pool.testpool.config;import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.time.Duration;/*** lettuce连接redis配置类* Lettuce 的 API 是线程安全的,所以不需要像Jedis那样采用JedisPool解决线程安全问题,执行的命令不是阻塞式命令性能差异小* 如果没有性能问题则不需要配置Lettuce连接池*/
@Configuration
public class LettuceConfig {/*** 读取spring.redis.host配置的值设置为host字段的值,如果没有该配置则使用默认值 127.0.0.1 (@Value注解默认值时字符串不需要加引号)*/@Value("${spring.redis.host:127.0.0.1}")private String host;@Value("${spring.redis.port:6379}")private Integer port;@Value("${spring.redis.host:lettuce1}")private String clientName;@Value("${spring.redis.database:0}")private Integer database;@Value("${spring.redis.connection-timeout:3000}")private Integer connectionTimeout;/*** 构建StatefulRedisConnection对象到spring容器中,对象名字为 simpleConnection* @return*/@Bean("simpleConnection")public StatefulRedisConnection<String,String> simpleConnection(){RedisURI redisURI = new RedisURI();redisURI.setClientName(clientName);redisURI.setHost(host);redisURI.setPort(port);redisURI.setDatabase(database);redisURI.setTimeout(Duration.ofMillis(connectionTimeout));RedisClient redisClient = RedisClient.create(redisURI);return redisClient.connect();}}

使用类:

package com.database.pool.testpool.controller;import io.lettuce.core.api.StatefulRedisConnection;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RequestMapping("/test")
@RestController
public class TestController {/*** 注入名字为simpleConnection的StatefulRedisConnection对象*/@Resource(name = "simpleConnection")private StatefulRedisConnection statefulRedisConnection;/*** lettuce 测试* @param value* @return*/@GetMapping("/info/{value}")public String info(@PathVariable("value") String value){statefulRedisConnection.sync().set("lettuce",value);return statefulRedisConnection.sync().get("lettuce").toString();}}

测试:
在这里插入图片描述
主从模式配置:
在这里插入图片描述
哨兵模式配置:
在这里插入图片描述
Cluster模式配置:
在这里插入图片描述
连接池配置:
导入包:

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version>
</dependency>

github示例:
在这里插入图片描述

三、Redisson

Redisson 是一个在 Redis 的功能基础上实现的 Java 驻内存数据网格客户端。实现了分布式和可扩展的 Java 数据结构,提供很多分布式相关操作服务,如分布式锁,分布式集合,可通过 Redis 支持延迟队列。
优点:
1、实现了分布式特性和可扩展的 Java 数据结构,例如分布式锁,分布式集合,分布式对象,分布式远程调度等等高级功能,适合分布式开发
2、与 Lettuce 一样,基于 Netty 框架的事件驱动与 redis 通信,支持异步调用,性能高
3、Redisson 的 API 是线程安全的,所以可以使用单个 Redisson 连接来完成各种操作
4、支持读写分离,支持读负载均衡,在主从复制和 Redis Cluster 架构下都可以使用
5、内建 Tomcat Session Manager,为 Tomcat 6/7/8 提供了会话共享功能,可以与 Spring Session 集成,实现基于 Redis 的会话共享
6、相比于 Jedis、Lettuce 等基于 redis 命令封装的客户端,Redisson 提供的功能更加高端和抽象,Redisson 可以类比 Spring 框架,这些框架搭建了应用程序的基础框架和功能,可以显著提升开发效率,让开发者有更多的时间来关注业务逻辑
7、文档较丰富,有中文文档
缺点:
1、和 Jedis、Lettuce 客户端相比,基础功能较为简单,对字符串的支持比较差,不支持排序、事务、管道、分区等 Redis 特性
2、API 更加抽象,学习使用成本高

jar包:

        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.4</version></dependency>

配置信息:

spring:redis:host: 127.0.0.1   #redis服务端主机ipport: 6379   #redis端口号#password: 123456   #redis密码database: 0   #使用redis的哪个库,redis有16个库,默认是0即第一个库connection-timeout: 3000    #建立连接超时时间,毫秒timeout: 3000   #命令等待超时时间,毫秒client-name: redissonPool  #客户端名称retry-attempts: 3  #命令失败重试次数,如果尝试达到 retry-attempts 仍然不能将命令发送至某个指定的节点时,将抛出错误retry-interval: 2000  #命令重试发送时间间隔,毫秒connection-pool-size: 20  #连接池连接数量idle-connection-timeout: 1800000  #空闲连接超时时间,毫秒connection-minimum-idle-size: 10  #最小空闲连接数量

配置类:

package com.database.pool.testpool.config;import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private String port;@Value("${spring.redis.database}")private Integer database;@Value("${spring.redis.connection-timeout}")private Integer connectionTimeout;@Value("${spring.redis.timeout}")private Integer timeout;@Value("${spring.redis.client-name}")private String clientName;@Value("${spring.redis.retry-attempts:3}")private Integer retryAttempts;@Value("${spring.redis.retry-interval:2000}")private Integer retryInterval;@Value("${spring.redis.connection-pool-size}")private Integer connectionPoolSize;@Value("${spring.redis.idle-connection-timeout}")private Integer idleConnectionTimeout;@Value("${spring.redis.connection-minimum-idle-size}")private Integer connectionMinimumIdleSize;@Beanpublic RedissonClient initRedissonClient(){Config config = new Config();//单节点配置config.useSingleServer().setAddress("redis://" + host + ":" + port).setDatabase(database).setConnectTimeout(connectionTimeout).setTimeout(timeout).setClientName(clientName).setRetryAttempts(retryAttempts).setRetryInterval(retryInterval).setConnectionPoolSize(connectionPoolSize).setIdleConnectionTimeout(idleConnectionTimeout).setConnectionMinimumIdleSize(connectionMinimumIdleSize);//设置序列化config.setCodec(JsonJacksonCodec.INSTANCE);//主从配置//config.useMasterSlaveServers().setMasterAddress("").setPassword("").addSlaveAddress(new String[]{"",""});//哨兵配置//config.useSentinelServers();//集群配置//config.useClusterServers().addNodeAddress();return Redisson.create(config);}
}

使用类:

package com.database.pool.testpool.controller;import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/test")
@RestController
public class TestController {@Autowiredprivate RedissonClient redissonClient;/*** redisson 测试* @param value* @return*/@GetMapping("/info/{value}")public String info(@PathVariable("value") String value){redissonClient.getBucket("redisson").set(value);Object redisson = redissonClient.getBucket("redisson").get();return redisson.toString();}}

测试:
在这里插入图片描述
如果启动时出现这个错误:
在这里插入图片描述
是由于 redisson 版本导致的,刚开始是 3.2.0 版本会有以上报错,后面更换为 3.12.4 版本就可以正常启动。

也可以使用 redisson-spring-boot-starter 的自动装配+配置实现集成。

三者对比:
1、Jedis 和 Lettuce 是比较纯粹的 Redis 命令客户端,几乎没提供什么分布式操作服务。
2、Jedis 和 Lettuce 两者相比,Jedis 需要JedisPool来解决线程安全问题,其他方面并没有太明显的区别,如果不需要使用 Redis 的高级功能,优先推荐使用 Lettuce。
3、相比于 Jedis、Lettuce 而言,Redisson 提供的功能更加高端和抽象,特别是分布式功能的支持提供了很多开箱即用的 Redis 高级功能,如果应用中需要使用到 Redis 的高级功能,比如分布式锁,分布式对象,分布式会话共享等等,建议使用 Redisson。
总结:
如果项目中对分布式功能的需求场景不多,推荐使用 Lettuce或Jedis。
如果项目中除了对基本的数据缓存操作需求以外,还需要用到分布式锁等功能,推荐采用Lettuce + Redisson组合方式使用(使用Lettuce弥补Redisson对于基础功能支持的不足,为了方便切换Jedis和Lettuce可以通过RedisTemplate来使用Jedis或Lettuce)。

四、RedisTemplate

RedisTemplate是Spring Data Redis框架提供的对Jedis和Lettuce的封装客户端,本质上还是使用Jedis或Lettuce,spring boot1.x的版本默认采用Jedis实现,spring boot2.x的版本默认采用Lettuce实现;可以方便的在Jedis和Lettuce之间切换具体的客户端实现;和日志门面与日志实现框架的关系一样,日志门面统一了操作日志的api,而具体日志的记录交给日志实现框去做,这样在切换日志实现时不用修改日志相关代码;RedisTemplate性能上不及Jedis,使用RedisTemplate时项目中至少需要有Jedis或Lettuce客户端之一的依赖包,否则会报错,RedisTemplate会自动根据项目中依赖的客户端选择底层使用Jedis还是Lettuce。
jar包:

<!-- 导入spring data redis的starter包,可以采用spring boot的自动装配机制,配置文件做相关配置就可以直接使用,2.0之后默认使用lettuce并且该包中已包含lettuce因此只需要导入这一个包就可以使用redisTemplate,如果要切换为jedis,那么排除该包中的lettuce包并导入jedis的包,会根据项目中包含的是lettuce、jedis进行自动选择对应redis底层客户端--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><!-- 版本可不写,spring boot内部已定义对应版本 --><version>2.3.5.RELEASE</version></dependency><!-- 使用lettuce时并且配置lettuce连接池需要导入该包 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version></dependency>

配置信息:

spring:redis:host: 127.0.0.1   #redis服务端主机ipport: 6379   #redis端口号#password: 123456   #redis密码database: 0   #使用redis的哪个库,redis有16个库,默认是0即第一个库client-name: ceshi    #生成的客户端实例名称(jedis连接的名称)timeout: 3000   #连接redis超时时间lettuce:  #lettuce连接池配置,lettuce连接池的配置需要 commons-pool2 包的支持,如果不使用lettuce连接池不需要导入该包pool:min-idle: 4  #最小空闲连接数max-idle: 8  #最大空闲连接数max-active: 8  #最大连接活跃数max-wait: 3000  #最长等待连接池分配连接时间

序列化配置:

package com.database.pool.testpool.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** redisTemplate序列化配置* 不配置序列化会导致redisTemplate设置的key和value有十六进制编码信息*/
@Configuration
public class RedisTemplateSerializeConfig {@Autowiredprivate RedisTemplate redisTemplate;@Beanpublic RedisTemplate redisTemplateSerialize(){//指定redisTemplate的key和value的序列化实例对象,由于hash类型的有key,value由filed和value组成,因此可以针对hash类型的key和value单独设置序列化StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();redisTemplate.setKeySerializer(stringRedisSerializer);redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(stringRedisSerializer);redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);return redisTemplate;}}

使用类:

package com.database.pool.testpool.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/test")
@RestController
public class TestController {@Autowiredprivate RedisTemplate redisTemplate;/*** redisTemplate 测试* @param value* @return*/@GetMapping("/info/{value}")public String info(@PathVariable("value") String value){redisTemplate.opsForValue().set("redisTemplate",value);return redisTemplate.opsForValue().get("redisTemplate").toString();}}

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

如果不配置redisTemplate序列化会有问题(虽然通过redisTemplate api还是可以正常操作):
在这里插入图片描述
设置序列化之后,key前面的16进制编码不会自动添加上:
在这里插入图片描述
redisTemplate底层使用lettuce客户端时,如果配置了lettuce连接池,没有导入 commons-pool2 包时报错如下:
在这里插入图片描述
以上是针对使用redisTemplate时底层使用lettuce客户端时的配置,如果使用redisTemplate底层使用Jedis客户端则做如下配置:
jar包(同样需要spring-boot-starter-data-redis包,但需要排除这个包里面依赖的lettuce的包并且导入Jedis的包):

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><!-- 版本可不写,spring boot内部已定义对应版本 --><version>2.3.5.RELEASE</version><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><!-- 导入jedis依赖包 --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.3.0</version></dependency>

配置信息:

spring:redis:host: 127.0.0.1   #redis服务端主机ipport: 6379   #redis端口号#password: 123456   #redis密码database: 0   #使用redis的哪个库,redis有16个库,默认是0即第一个库client-name: ceshi    #生成的客户端实例名称(jedis连接的名称)timeout: 3000   #连接redis超时时间jedis:  #jedis连接池配置pool:min-idle: 4  #最小空闲连接数max-idle: 8  #最大空闲连接数max-active: 8  #最大连接活跃数max-wait: 3000  #最长等待连接池分配连接时间

序列化及使用的代码和redisTemplate使用lettuce客户端一致。
测试:
在这里插入图片描述
将spring-boot-starter-data-redis包中依赖的Lettuce包排出后,如果不导入Jedis或Lettuce的依赖,项目启动报错如下:
在这里插入图片描述
哨兵模式配置:
在这里插入图片描述
集群模式配置:
在这里插入图片描述
为了减少操作redis的代码可以使用SpringCache功能自动化将数据缓存在redis中,并优先读取redis缓存;但这种功能有局限性,方法加上注解后会自动根据key缓存查询到的数据,并从缓存中读取这个key的值;只能适用于简单场景,如果对于key的一些特殊场景的操作则还是需要单独写代码处理。

redis的一些建议:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

缓存穿透解决方案(缓存中没有数据,查询数据库但数据是不存在的导致数据库也查询不到数据,就不能将数据缓存在redis中,这样每次查询这样的数据都会查询redis和数据库,导致缓存失效;重点是查询的数据是本来就不存在的):
在这里插入图片描述
缓存击穿解决方案(某个热点的key缓存失效后(比如缓存时间到了或数据做了修改删除了缓存中的数据)这样一瞬间查询这个key的请求会全部打到数据库上,就叫做缓存击穿;重点是查询的数据是存在的,只是缓存失效了):
在这里插入图片描述
核心是延迟缓存过期或缓存失效后通过加锁让一个线程查询数据库并重构热点key的缓存,其他线程则读取重构后的缓存(加锁时通过双重检查的模式让后续的线程从缓存中读取数据)。

缓存雪崩解决方案(缓存击穿的升级版,多个热点key同时失效,导致多个热点key的请求全部请求数据库;所以缓存击穿的解决方案在缓存雪崩中针对单个key的处理方式也适用):
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
注意:redis客户端模式主要作用是更新redis缓存后,redis会自动通知本地缓存更新;这个功能在redis6.x版本支持。
在这里插入图片描述

在这里插入图片描述
文章部分内容参考视频

相关文章:

spring boot整合常用redis客户端(Jedis、Lettuce、RedisTemplate、Redisson)常见场景解决方案

Java操作redis有三种客户端供选择&#xff1a;Jedis、Lettuce、Redisson。 在实际项目中运用最多的客户端还是Redisson、RedisTemplate&#xff1b;其中RedisTemplate并非是一个新的redis客户端实现&#xff0c;RedisTemplate是Spring Data Redis中提供的封装好的redis操作模板…...

HarmonyOS之运行Hello World

目录 下载与安装DevEco Studio 配置环境 创建项目 认识DevEco Studio界面 运行Hello World 了解基本工程目录 工程级目录 模块级目录...

postgresql数据库|wal日志的开启以及如何管理

一&#xff0c; wal的基本概念 WAL即Write Ahead Log预写式日志,简称wal日志,相当于oracle中的redo日志。只是oracle中redo是固定几个redo日志文件,然后轮着切换去写入。pg中wal日志是动态切换,单个wal日志写满继续写下一个wal日志&#xff0c;连续不断生成wal日志。&#xf…...

小波变换学习笔记【1】

【声明】本博客为学习B站视频小波分解与重构所做笔记&#xff0c;供自己和大家查阅学习&#xff0c;想查看 up 原视频请移步 B 站&#xff0c;侵删。 1.1 小波变换的由来 傅里叶变换基本思想&#xff1a;将信号分解成一系列不同频率的连续正弦波的叠加。 其缺点是&#xff0c;…...

雷柏mv20鼠标使用体验

用了1年多&#xff0c;第一次用竖着的鼠标&#xff0c;现在已经很习惯了&#xff0c;感觉还不错。说说使用感受&#xff1a; 1、 仍然是长时间使用鼠标&#xff0c;但是很少出现手腕痛的情况&#xff0c;确实是有一定效果的。 2、使用场景是有限制的&#xff0c;我是配合笔记…...

【分布式云储存】Springboot微服务接入MinIO实现文件服务

文章目录 前言技术回顾准备工作申请accessKey\secretKey创建数据存储桶公共资源直接访问测试 接入springboot实现文件服务依赖引入配置文件MinIO配置MinIO工具类 OkHttpSSLSocketClient兼容ssl静态资源预览解决方案资源上传预览测试测试结果 前言 上篇博客我们介绍了分布式云存…...

机器人中的数值优化|【四】L-BFGS理论推导与延伸

机器人中的数值优化|【四】L-BFGS理论推导与延伸 往期内容回顾 机器人中的数值优化|【一】数值优化基础 机器人中的数值优化|【二】最速下降法&#xff0c;可行牛顿法的python实现&#xff0c;以Rosenbrock function为例 机器人中的数值优化|【三】无约束优化&#xff0c;拟牛…...

ThemeForest – Canvas 7.2.0 – 多用途 HTML5 模板

ThemeForest 上的 HTML 网站模板受到全球数百万客户的喜爱。与包含网站所有页面并允许您在 WP 仪表板中自定义字体和样式的 WordPress 主题不同&#xff0c;这些设计模板是用 HTML 构建的。您可以在 HTML 编辑器中编辑模板&#xff0c;但不能在 WordPress 上编辑模板&#xff0…...

本地部署 川虎 Chat

本地部署 川虎 Chat 1. 川虎 Chat 项目概述2. Github 地址3. 部署 川虎 Chat4. 配置 config.json5. 启动 川虎 Chat 1. 川虎 Chat 项目概述 为ChatGPT等多种LLM提供了一个轻快好用的Web图形界面和众多附加功能。 支持 GPT-4 基于文件问答 LLM本地部署 联网搜索 Agent 助理…...

IntelliJ IDEA 控制台中文乱码的四种解决方法

前言 IntelliJ IDEA 如果不进行配置的话&#xff0c;运行程序时控制台有时候会遇到中文乱码&#xff0c;中文乱码问题非常严重&#xff0c;甚至影响我们对信息的获取和程序的跟踪。开发体验非常不好。 本文中我总结出四点用于解决控制台中文乱码问题的方法&#xff0c;希望有助…...

23岁准备转行嵌入式

前端难找工作&#xff0c;而且感觉没有前景&#xff0c;我觉得我就算入行也不会干多久&#xff0c;软件开发让我感觉不到实在的东西。小时候喜欢去动手制作一些玩具&#xff0c;我对实在一些的东西感兴趣一些例如手表&#xff0c;小机器人等等一些。我有保持坚持学习下去的动力…...

http请求报错:406 Not Acceptable的解决办法

目录 应用场景 基本概念 解决方法 方法一&#xff1a; 方法二&#xff1a; 方法三&#xff1a; 应用场景 接口在返回结果集的时候出现了406的报错&#xff0c;但是返回String类型不会报错&#xff0c;正常返回。 基本概念 406 Not Acceptable是一个HTTP响应状态码&…...

信息化发展75

数字化治理 数字化治理通常指依托互联网、大数据、人工智能等技术和应用&#xff0c;创新社会治理方法与手段&#xff0c;优化社会治理模式&#xff0c;推进社会治理的科学化、精细化、高效化&#xff0c;助力社会治理现代化。数字化治理是数字经济的组成部分之一&#xff0c;…...

C++八股

1、简述一下C中的多态 在面向对象中&#xff0c;多态是指通过基类的指针或引用&#xff0c;在运行时动态调用实际绑定对象函数的行为&#xff0c;与之相对应的编译时绑定函数称为静态绑定。 静态多态 静态多态是编译器在编译期间完成的&#xff0c;编译器会根据实参类型来选择…...

Nat. Commun. | 大规模高分辨单光子成像

本文由论文作者团队(课题组)投稿 单光子雪崩二极管(Single Photon Avalanche Diode,简称SPAD)阵列因其极佳的单光子灵敏度而受到广泛关注,已广泛应用于量子通信与计算、荧光寿命成像、时间飞行成像等各个领域。与同样具有较高灵敏度的EMCCD和sCMOS相比,SPAD阵列能够在极…...

Android开源库

见&#xff1a;GitHub - eHackyd/Android_OpenSourceLibrary: Android开源库的学习笔记...

【小程序 - 基础】页面导航、页面事件、生命周期、WXS脚本_04

目录 一、页面导航 1. 什么是页面导航 2. 小程序中实现页面导航的两种方式 2.1 声明式导航 2.1.1 导航到 tabBar 页面 2.1.2 导航到非 tabBar 页面 2.1.3 后退导航 2.2 编程式导航 2.2.1 导航到 tabBar 页面 2.2.2 导航到非 tabBar 页面 2.2.3 后退导航 2.3. 导航…...

矩阵求导数

矩阵 A ∣ 1 2 1 2 − 1 3 ∣ , 计算 f ( x ) ∣ ∣ A x ∣ ∣ 2 ∣ ∣ x ∣ ∣ 2 的最大值。 矩阵A \begin {vmatrix} 1 & 2 & 1\\2 & -1 & 3 \end {vmatrix},计算f(x) \frac{||Ax||_2}{||x||_2}的最大值。 矩阵A ​12​2−1​13​ ​,计算f(x)∣∣x∣∣2…...

竞赛 大数据疫情分析及可视化系统

文章目录 0 前言2 开发简介3 数据集4 实现技术4.1 系统架构4.2 开发环境4.3 疫情地图4.3.1 填充图(Choropleth maps)4.3.2 气泡图 4.4 全国疫情实时追踪4.6 其他页面 5 关键代码最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 大数据疫…...

数据结构--栈

线性表的定义 前面文章有讲过&#xff0c;线性表就是一次保存单个同类型元素&#xff0c;多个元素之间逻辑上连续 例子&#xff1a;数组&#xff0c;栈&#xff0c;队列&#xff0c;字符串 栈 1.1 栈和队列的特点 栈和队列都是操作受限的线性表。 前面学过的数组&#xff0c;…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...

32单片机——基本定时器

STM32F103有众多的定时器&#xff0c;其中包括2个基本定时器&#xff08;TIM6和TIM7&#xff09;、4个通用定时器&#xff08;TIM2~TIM5&#xff09;、2个高级控制定时器&#xff08;TIM1和TIM8&#xff09;&#xff0c;这些定时器彼此完全独立&#xff0c;不共享任何资源 1、定…...

2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案

一、延迟敏感行业面临的DDoS攻击新挑战 2025年&#xff0c;金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征&#xff1a; AI驱动的自适应攻击&#xff1a;攻击流量模拟真实用户行为&#xff0c;差异率低至0.5%&#xff0c;传统规则引…...

大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程

基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...

用鸿蒙HarmonyOS5实现国际象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的国际象棋小游戏的完整实现代码&#xff0c;使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├── …...

篇章一 论坛系统——前置知识

目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构​编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...

机器学习复习3--模型评估

误差与过拟合 我们将学习器对样本的实际预测结果与样本的真实值之间的差异称为&#xff1a;误差&#xff08;error&#xff09;。 误差定义&#xff1a; ①在训练集上的误差称为训练误差&#xff08;training error&#xff09;或经验误差&#xff08;empirical error&#x…...