几种常用的 Redis 的 java 客户端:
Jedis : 以 Redis 命令作为方法名称,学习成本低,简单实用。但是 Jedis 实例是线程不安全的,多线程环境下需要基于连接池来使用
Lettuce : Lettuce 是基于 Netty 实现的,支持同步、异步和响应式编程方式,并且是线程安全的。支持 Redis 的哨兵模式、集群模式和管道模式。
Redisson : Redisson 是一个基于 Redis 实现的分布式、可伸缩的 Java 数据结构集合。包含了诸如 Map、Queue、Lock、 Semaphore、AtomicLong 等强大功能
# Jedis
引入依赖
1
2
3
4<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>建立链接
如果我们没有密码的话,我们就把第二条注释掉就可以了,否则就会报错
1
2
3
4
5
6
7
8
9
10
11private Jedis jedis;
//测试类中任何一个测试方法执行之前都先执行该注解标注的方法
void contextLoads() {
// 1.建立连接
jedis = new Jedis("127.0.0.1",6379);
// 2.设置密码
jedis.auth("root");
// 3.选择库 如果我们不选择的话,默认就是选择的0号库
jedis.select(0);
}测试 String
1
2
3
4
5
6
7
8
9
void testString(){
// 存入数据
String result = jedis.set("name","asialee");
System.out.println("result:"+result);
// 获取数据
String name = jedis.get("name");
System.out.println("name:"+name);
}释放资源
1
2
3
4
5
6
7
void tearDown(){
// 释放资源
if(jedis !=null){
jedis.close();
}
}
Jedis 使用的基本步骤:
- 引入依赖
- 创建 Jedis 对象,建立连接
- 使用 Jedis,方法名与 Redis 命令一致
- 释放资源
# Jedis 连接池
Jedis 本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用 Jedis 连接池代替 Jedis 的直连方式
1 | public class JedisConnectionFactory { |
然后我们再改一下我们的建立连接池
1 | //测试类中任何一个测试方法执行之前都先执行该注解标注的方法 |
到现在的小伙伴可能在想,那我们最后执行 close 方法的时候,不还是销毁了吗?
答案:不是销毁了
我们看一下 close 的原码文件,当判断我们有连接池的时候就是进入到第一个 if,底层并不会真正的 close,而是归还 return 出去了
1 | pool.returnBrokenResource(this); |
# 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 实现
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 类型数据 |
redisTemplate | 通用的命令 |
SpringBoot 已经提供了对 SpringDataRedis 的支持,使用非常简单:
引入依赖
1
2
3
4
5
6
7
8
9
10<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--common-pool连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>配置文件
1
2
3
4
5
6
7
8
9
10
11spring:
redis:
host: 192.168.150.101
port: 6379
password: 123321
lettuce:
pool:
max-active: 8 # 最大连接
max-idle: 8 # 最大空闲连接
min-idle: 0 # 最小空闲连接
max-wait: 100ms # 连接等待时间注入 RedisTemplate
1
2
private RedisTemplate redisTemplate;编写测试
1
2
3
4
5
6
7
8
9
10
11
12
public class RedisTest {
private RedisTemplate redisTemplate;
void testString(){
redisTemplate.opsForValue().set("name","asialee");
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
}
SpringDataRedis 的使用步骤:
- 引入 spring-boot-starter-data-redis 依赖
- 在 application.yml 配置 Redis 信息
- 注入 RedisTemplate
# SpringDataRedis 的序列化方式
RedisTemplate 可以接收任意 Object 作为值写入 Redis,只不过写入前会把 Object 序列化为字节形式,默认是采用 JDK 序列化,得到的结果是这样的:
缺点:
- 可读性差
- 内存占用较大
我们可以自定义 RedisTemplate 的序列化方式,代码如下
1 |
|
# StringRedisTemplate
尽管 JSON 的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON 序列化器会将类的 class 类型写入 json 结果中,存入 Redis,会带来额外的内存开销。
为了节省内存空间,我们并不会使用 JSON 序列化器来处理 value,而是统一使用 String 序列化器,要求只能存储 String 类型的 key 和 value。当需要存储 Java 对象时,手动完成对象的序列化和反序列化。
Spring 默认提供了一个 StringRedisTemplate 类,它的 key 和 value 的序列化方式默认就是 String 方式。省去了我们自定义 RedisTemplate 的过程:
1 |
|
RedisTemplate 的两种序列化实践方案:
方案一:
- 自定义 RedisTemplate
- 修改 RedisTemplate 的序列化器为 GenericJackson2JsonRedisSerializer
方案二:
- 使用 StringRedisTemplate
- 写入 Redis 时,手动把对象序列化为 JSON
- 读取 Redis 时,手动把读取到的 JSON 反序列化为对象