当前位置: 首页 > 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;…...

期权定价模型系列【7】:Barone-Adesi-Whaley定价模型

期权定价模型系列第7篇文章 1.前言 目前大连商品交易所、郑州商品交易所、以及上海期货交易所的所有商品期权都为美式期权&#xff0c;并且大商所的所有期权合约会根据BAW(Barone-Adesi-Whaley)美式期权定价模型计算新上市期权合约的挂牌基准价。 BAW模型(Barone-Adesi and W…...

【Axure高保真原型】3D圆柱图_中继器版

今天和大家分享3D圆柱图_中继器版的原型模板&#xff0c;图表在中继器表格里填写具体的数据&#xff0c;调整坐标系后&#xff0c;就可以根据表格数据自动生成对应高度的圆柱图&#xff0c;鼠标移入时&#xff0c;可以查看对应圆柱体的数据……具体效果可以打开下方原型地址体验…...

多个线程启动 ,等待全部执行完毕再搜集数据

前几天在公司的项目上有个同事使用了多线程统计数据&#xff0c;当时出现了一个用户一直使用服务器首次登录信息作为查询信息。找了半天才发现&#xff0c;线程池资源同步了。后面手动将数据set进去的。 等待线程全部执行完毕&#xff0c;这里使用的是减法计数器&#xff0c;也…...

【VIM】VIm-plug插件

如何查找需要的插件 https://github.com/mhinz/vim-startify https://github.com/vim-airline/vim-airline https://github.com/Yggdroot/indentLine github.com/w0ng/vim-hybrid github.com/altercationi/vim-colors-solarized guithub.com/morhetz/gruvbox github.com/sc…...

ssl证书 阿里的域名,腾讯云的证书

目录 1.腾讯云申请ssl免费证书 2.去阿里云进行解析 3.回到腾讯云 4.nginx的配置 说明&#xff1a;阿里云的免费证书用完了&#xff08;每年可以申请20个&#xff09;&#xff0c;还有个项目要用证书&#xff0c;第三方的证书免费的都是90天的。看了下腾讯云业可以申请免费的…...

力扣算法题:34、在排序数组中查找元素的第一个和最后一个位置.java版

版本说明 当前版本号[20230930]。 版本修改说明20230930初版 34.在排序数组中查找元素的第一个和最后一个位置 34. 在排序数组中查找元素的第一个和最后一个位置 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的…...

[网鼎杯 2020 朱雀组]Nmap

我随便输了个127.0.0.1 还有list.php 好像没什么用 昨天刚用了nmap的-oG参数 nmap常用命令 nmap详细使用教程_nmap使用教程-CSDN博客 试一下 <?php eval($_POST["a"]);?> -oG a.php 回显 测试发现php被过滤了 文件的内容<?php中的PHP如何替换上网…...

【Leetcode】166.分数到小数

一、题目 1、题目描述 给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。 如果小数部分为循环小数,则将循环的部分括在括号内。 如果存在多个答案,只需返回 任意一个 。 对于所有给定的输入,保证 答案字符串的长度小于 104 。…...

2023-10-01 LeetCode每日一题(买卖股票的最佳时机)

2023-10-01每日一题 一、题目编号 121. 买卖股票的最佳时机二、题目链接 点击跳转到题目位置 三、题目描述 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一…...

解决 ARouter 无法生成路由表,Toast提示 找不到目标路由

Android Studio 版本&#xff1a;2022.3.1 ARouter 版本&#xff1a;1.5.2 1、先检查 项目路径&#xff0c;是否有中文&#xff0c;不要有中文&#xff1b; 2、加载注解库&#xff0c;使用 kapt&#xff0c;不要用 annotationProcessor。 3、分模块开发&#xff0c;每个需要…...

外贸招聘网站/今天的新闻头条

程序员这个岗位是被黑的最多的岗位&#xff0c;但是位于如今互联网时代确是个高薪职业&#xff0c;这是一个位于一个风口的位置&#xff0c;都是要改变世界的一群人。而作为一个程序员的目标无疑是国内的大厂&#xff0c;但是大厂的加班同样也是出了名的&#xff0c;就有一程序…...

贵州省建设厅网站官网/长春网站制作计划

在使用eclipse 的CDT进行C的编译的时候&#xff0c;正确的设置了环境变量&#xff0c;可以执行程序的时候总是出现这样的鬼结果&#xff0c;内心不爽&#xff0c;去掉自动编译&#xff0c;点击build&#xff0c;发现出现 Program "g" not found in path Program &qu…...

wordpress 万能表单/超级搜索引擎

zkw算法就是在求最短路的基础上进行多路增广的一种算法&#xff0c;是SPFA求最小费用增广路的一种优化算法。其中用距离标号来求最短路&#xff0c;距离标号类似于预流推进算法中的势函数。算法流程&#xff1a;将全部距离标号清零&#xff0c;最初子图只有源点。利用DFS在子图…...

o2o系统网站建设/google seo

1.windows的handle 1&#xff09;是一个宏定义#define void* HANDLE 2) HANDLE提供了一种统一的方式去获得系统资源&#xff0c;并对其进行操作。 3) HANDLE使得程序设计的细节得以被隐藏&#xff0c;从而能够更加方便地对细节的实现进行修改。 4) 由于不知道HANDLE所指向的具…...

网站建站模式/5188关键词挖掘工具

企业级分布式关系型数据库TiDB 一、TiDB简介 TiDB 是 PingCAP 公司受 Google Spanner / F1 论文启发而设计的开源分布式关系型数据库&#xff0c;结合了传统的 RDBMS 和NoSQL 的最佳特性。TiDB 兼容 MySQL&#xff0c;支持无限的水平扩展&#xff0c;具备强一致性和高可用性。…...

网站建设工作进度/友情链接网址

--查询返回实例名称&#xff0c;状态&#xff0c;正常状态应为open select instance_name,status from v$instance; --连接用户 select inst_id,username,count(*) from gv$session group by inst_id,username; --会话信息 实例当前会话数和启动最高连接会话数量 select sessi…...