CentOS 7.6
Redis有多种客户端操作方法,本博文主要介绍两种常用的操作方法。使用jedis
来操作客户端。并且考虑到spring
的流行程度,所以也对SpringDataRedis
操作的方式进行了详细的说明。
具体的说明看博文
在上两篇博文的学习中,我们知道官网给了我们很多的信息。通过在Redis
官方网站有各种操作redis
客户端的说明:
我们只看JAVA
是如何通过简单又轻便的方式来操作redis
的。
并且,对于jedis
和lettuce
是Spring Data Redis
所支持的两种操作方式,并且lettuce
在spring data
中被作为是默认的操作方式。
对于Jedis
的操作方式,我们先来快速的入个门,Jedis
的 官网地址https://github.com/redis/jedis,按照管方所给的信息来一步步的搭建。
对于Jedis
操作方式的有点之一就是jedis
的各种API
非常的贴近于我们上篇博文所介绍的各种命令。
Jedis
以Redis
命令作为方法命令的方式,体现了简单易用,学习成本低。但是,有缺点是Jedis
实例是线程不安全的。如果是多线程环境下,需要基于连接池的方式来使用。我们先来看看朴素的方式,也就是没有考虑线程安全性的方式如何操作Redis
。
博主是通过单元测试的方式来验证的。步骤如下:
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>4.1.1version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiterartifactId>
<version>5.7.0version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-nopartifactId>
<version>1.7.2version>
dependency>
public class JedisTest {
private Jedis jedis;
@BeforeEach
void setup(){
// 1.建立连接
jedis = new Jedis("192.168.81.150", 6379);
// 2. 设置密码
jedis.auth("123321");
// 选用库
jedis.select(0);
}
// 关闭连接
@AfterEach
void teardown(){
if(jedis != null) jedis.close();
}
}
@Test
void testString(){
// 存入数据
String result = jedis.set("name", "fangshaolei");
System.out.println("result = " + result);
// 获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
void testHash(){
jedis.hset("user:1", "name", "fangshaolei");
jedis.hset("user:1", "age", "21");
//获取
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
Jedis
使用的基本步骤:
Jedis
对象,建立连接Jedis
,方法名与上篇博文Redis
命令一致我们知道,Jedis
本身是线程不安全的,并且频繁的建立和销毁连接会有性能的损耗,对于官方也推荐我们使用Jedis
连接池来代替上述方法中朴素Jedis
的直连方式。
其实连接池的操作和上述的步骤基本一致。获取Jedis
对象的方式由原来的创建,变成了从连接池中获取。
按照程序设计的思想,把连接池的获取操作创建为工具类。如下:
public class JedisConnectionFactory {
private static final JedisPool jedisPool;
static{
// 配置连接池
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(8);
config.setMaxIdle(8);
config.setMinIdle(0);
config.setMaxWaitMillis(1000);
jedisPool = new JedisPool(config,
"192.168.81.150",6379 , 1000,"123321" );
// 配置连接池的参数要切换
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
而对于使用,只需要把上述朴素Jedis
操作的方式改为从连接池中获取即可。
private Jedis jedis;
@BeforeEach
void setup(){
// 1.建立连接
// jedis = new Jedis("192.168.81.150", 6379);
jedis = JedisConnectionFactory.getJedis();
// 2. 设置密码
jedis.auth("123321");
// 选用库
jedis.select(0);
}
JedisPool
使用的基本步骤:
Jedis
对象,建立连接Jedis
,方法名与上篇博文Redis
命令一致SpringBoot
已经成为企业开发的标配,所以,在学习Java
新技术的时候,我们都要考虑一下,能不能基于SpringBoot来整合下Jedis的连接池呢?
SpringData
是Spring
中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis
,⚖️官网地址https://spring.io/projects/spring-data-redis,对于SpringDataRedis
的功能官网也进行了详细的说明,主要有以下几点:
Redis
客户端的整合(Lettuce
(默认)和Jedis
)RedisTemplate
统一API
来操作Redis
Redis
的发布订阅模型Redis
哨兵和Redis
集群Lettuce
的响应式编程JDK
、JSON
、字符串
、Spring
对象的数据序列化及反序列化Redis
的JDKCollection
实现对于以后的博文如果设计到项目部分,都是基于springboot来进行展示和说明。
其中,对于SpringDataRedis
提供了RedisTemplate
工具类,其中封装了各种对Redis
的操作。
但是,SpringDataRedis
不在像Jedis
一样,各个命令作为方法名。而是有点类似于官方查阅文档的形式,将不同数据类型的操作API封装到了不同的类型中:
和朴素Jedis
操作类似,直接先开始快速入门:
SpringBoot
已经提供了对SpringDataRedis
的支持,使用非常简单:
在建立SpringBoot
项目的时候,在NoSQL
勾选上redis
,但是,由于SpingBoot
默认支持连接池的方式连接,所以,还要加上一个连接池依赖。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
在配置文件中配置连接redis
的一些必要信息。
这里我直接使用lettuce客户端的方式操作,如果是redis还需要配置一些额外信息。
spring:
redis:
host: 192.168.81.150
port: 6379
password: 123321
lettuce:
pool:
max-wait: 100
min-idle: 8
max-idle: 8
max-active: 8
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("username", "fangshaolei");
// 获取名字
Object username = redisTemplate.opsForValue().get("username");
System.out.println("username = " + username);
}
SpringDataRedis
的使用步骤:
spring-boot-starter-data-redis
依赖application.yml
配置Redis
信息RedisTemplate
SpringDataRedis
的两种序列方案RedisTemplate
可以接收任意Object
作为值写入Redis
,只不过写入前会把Object
序列化为字节形式,默认是采用JDK序列化
,得到的结果是这样的:
缺点也暴露的很明显:
但是,在SpringBoot
中给我们支持了强大的组件定制服务。我们可以自定义RedisTemplate的序列化方式,代码如下:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
// 创建redistemplate对象
RedisTemplate redisTemplate = new RedisTemplate();
// 设置连接工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// key hashkey采用string的方式序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.json());
// value hashvalue采用json序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
redisTemplate.setHashValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
我们在通过一个方法来进行测试,测试代码如下:
@Test
void testObject(){
redisTemplate.opsForValue().set("user:1", new User("fangshaolei", 21));
//
User o = (User) redisTemplate.opsForValue().get("user:1");
System.out.println("o = " + o);
}
注意: 这里可能会报错,找不到Jackson
依赖,本来SpringMVC
自动集成了Jackson
依赖,所以我们这里手动导入依赖:
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
dependency>
发现,在redis
中的数据已经通过我们自定义的序列化方式有所改变:
已经达到了我们的目的,但是尽管JSON
的序列化方式可以满足我们的需求,但仍然存在一些问题,如图所示:
由于SpringDataRedis
自动的给我们进行序列化和反序列化,为了操作的需要,在反序列化中知道对象的类型。所以,JSON
序列化容器会将类的CLASS类型写入到JSON
结果中,存入Redis
,会带来额外的内存开销。
那我们如何保证能够节省内存(即将@class
字段去除),又能保证反序列化成功呢?
唯一的方法,就是序列化和反序列化的操作,交给我们自己来做,我们也不必去配置反序列化方式。
也就是我们为了节省内存空间,我们**并不会使用JSON序列化器来处理value,**而是统一使用String序列化器
,要求只能存储String类型
的key
和value。当需要存储Java
对象时,手动完成对象的序列化和反序列化。
Spring
默认提供了一个StringRedisTemplate
类,它的key
和value
的序列化方式默认就是String
方式。省去了我们自定义RedisTemplate
的过程:
在代码中,我使用了private static final ObjectMapper mapper = new ObjectMapper();
的序列化工具来进行序列化。读者可以自行选择序列化工具。
@SpringBootTest
class StringTemplateTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
void contextLoads() {
stringRedisTemplate.opsForValue().set("username", "son of sun");
// 获取名字
Object username = stringRedisTemplate.opsForValue().get("username");
System.out.println("username = " + username);
}
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testObject() throws JsonProcessingException {
User user = new User("fangshaolei", 21);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("user:100",json );
// 获取数据
String jsonUser = stringRedisTemplate.opsForValue().get("user:100");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
@Test
public void testHash(){
stringRedisTemplate.opsForHash().put("user:101", "name", "fangshaolei");
stringRedisTemplate.opsForHash().put("user:101", "age", 12);
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:101");
System.out.println("entries = " + entries);
}
}
按照上述所介绍的,对两种方案进行总结。
RedisTemplate
RedisTemplate
的序列化器为GenericJackson2JsonRedisSerializer
StringRedisTemplate
Redis
时,手动把对象序列化为JSON
Redis
时,手动把读取到的JSON
反序列化为对象对于博主,我推荐使用方案二作为springboot
操作redis
的方式。优点有很多。
本博文对jedis
的操作和SpringDataRedis
的操作都进行了详细的解释和演示,并对在springboot
整合redis
的方式中说明了两种序列化方案。
其中第二种方案是开发中所推荐的方案,至此我们已经学会了开发中的关键一步————SpringBoot
整合redis
,后续可能会有小demo
来演示,强化理解。
文章还有许多不足之处,欢迎指正。
【参考博文】
https://blog.csdn.net/qq_36781505/article/details/86612988
https://www.cnblogs.com/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html
https://www.jianshu.com/p/a1038eed6d44
https://www.cnblogs.com/liuling/p/2014-4-19-04.html