目前我了解到的有以下 4 种。

Spring Data Redis

1.介绍

SpringData 是 Spring 中数据操作的模块,包含对各种数据库的集成,相当于定义了一些如增删改查的抽象接口,继而有对应不同数据库的实现。

其中对 redis 集成模块就叫做 Spring Data Redis。

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

SpringDataRedis 中提供了 RedisTemplate 工具类,其中封装了各种对 Redis 的操作。并且将不同数据类型的操作 API 封装到了不同的类型中:

1652976773295

image-20220808215611419

2.使用

2.1 引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
      <!--连接池依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

2.2 编写配置文件

1
2
3
4
5
6
7
8
9
10
11
spring:
redis:
host: localhost
port: 6379
password: yourPassword
lettuce:
pool:
max-active: 8 #最大连接
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
max-wait: 100ms #连接等待时间

2.3 测试与问题

image-20220808220320953

可以看到是可以正常写入和获取的,那么我们来看看数据库。

image-20220808220418512

发现问题:

  1. 插入进来的怎么不是“coderbin”,多了前面一坨东西呢?
  2. 为什么查不到值呢?

追溯问题:

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

image-20220808223410139

2.4 解决序列化问题

简单方法:直接不用 RedisTemplate,我们用StringRedisTemplate.

image-20220808223818068

正常输出,再去看数据库。

image-20220808223858701

键值的数据也是正常的,但是!这里又有问题。

image-20220808224023585

stringRedisTemplate 的 set 方法要求键值都为 string 类型,这就无法满足我们的需求。

那么我们就要自定义 RedisTemplate 的序列化方式啦!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建RedisTemplate对象
RedisTemplate<String, Object> 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;
}
}

image-20220808225246034

image-20220808225305305

可以看到已经大功告成了。

总结一下:

RedisTemplate 的两种序列化实践方案:

  • 方案一:

    • 自定义 RedisTemplate
    • 修改 RedisTemplate 的序列化器为 GenericJackson2JsonRedisSerializer
  • 方案二:

    • 使用 StringRedisTemplate
    • 写入 Redis 时,手动把对象序列化为 JSON
    • 读取 Redis 时,手动把读取到的 JSON 反序列化为对象

Jedis

(25 条消息) Jedis 常用方法 API_遛狗大师的博客-CSDN 博客_jedis 方法

使用阻塞的 I/O,且其方法调用都是同步的(慢!),程序流需要等到 sockets 处理完 I/O 才能执行,不支持异步。Jedis 客户端实例不是线程安全的,所以需要通过连接池来使用 Jedis。

1.快速入门

1.1 引入依赖

1
2
3
4
5
6
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>

1.2 创建连接池

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

有关池化思想,并不仅仅是这里会使用,很多地方都有,比如说我们的数据库连接池,比如我们 tomcat 中的线程池,这些都是池化思想的体现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JedisConnectionFacotry {
private static final JedisPool jedisPool;

static {
//配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
//创建连接池对象
jedisPool = new JedisPool(poolConfig,
"localhost",6379,1000);
}

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

1.3 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@BeforeEach
void setUp(){
//建立连接
/*jedis = new Jedis("127.0.0.1",6379);*/
jedis = JedisConnectionFacotry.getJedis();
//选择库
jedis.select(0);
}
@Test
void testHash() {
// 插入hash数据
jedis.hset("user:1", "name", "Jack");
jedis.hset("user:1", "age", "21");

// 获取
Map<String, String> map = jedis.hgetAll("user:1");
System.out.println(map);
}
@AfterEach
void tearDown() {
if (jedis != null) {
jedis.close();
}
}

image-20220808230515826

image-20220808230527191

测试成功!

Redisson

Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的 Java 常用对象,还提供了许多分布式服务。Redisson 的宗旨是促进使用者对 Redis 的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

1.快速入门

1.1 引入依赖

1
2
3
4
5
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.6</version>
</dependency>

1.2 配置 Redisson 客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class RedissonConfig {

@Bean
public RedissonClient redissonClient(){
// 配置
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.150.101:6379")
.setPassword("123321");
// 创建RedissonClient对象
return Redisson.create(config);
}
}

1.3 使用 Redission 的分布式锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Resource
private RedissionClient redissonClient;

@Test
void testRedisson() throws Exception{
//获取锁(可重入),指定锁的名称
RLock lock = redissonClient.getLock("anyLock");
//尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位
boolean isLock = lock.tryLock(1,10,TimeUnit.SECONDS);
//判断获取锁成功
if(isLock){
try{
System.out.println("执行业务");
}finally{
//释放锁
lock.unlock();
}

}
}

Redisson 有非常多的分布式服务:

image-20220808232552996

具体的各个用法需要结合官网和你的具体业务实现。

Java 客户端总结

  • 如果使用了 Spring,并且没有过多的定制化要求,那么大可以选择 Spring Data Redis
  • 如果没有使用 Spring,只需要实现简单的业务,也没有过高的性能要求,那么可以选择 Jedis
  • 如果没有使用 Spring,并且追求高性能、高定制化,可以用 Lettuce,支持异步、连接池
  • 如果项目是分布式架构需要一些分布式服务(分布式锁,分布式集合等等),那么可以选择 Redisson