Redis的Java客户端
- 目前主流的Redis的Java客户端有三种
- Jedis和Lettuce:这两个主要是提供了Redis命令对应的API,方便我们操作Redis,而SpringDataRedis又对这两种做了抽象和封装,因此我们后期会直接以SpringDataRedis来学习。
- Redisson:是在Redis基础上实现了分布式的可伸缩的java数据结构,例如Map、Queue等,而且支持跨进程的同步机制:Lock、Semaphore等待,比较适合用来实现特殊的功能需求。
Jedis客户端
快速入门
-
使用Jedis的步骤
- 导入Jedis的maven坐标
XML <!--jedis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.7.0</version> </dependency> <!--单元测试--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.7.0</version> <scope>test</scope> </dependency>
- 建立连接
新建一个单元测试类
JAVA private Jedis jedis; @BeforeEach void setUp() { //1. 建立连接 jedis = new Jedis("101.42.225.160", 6379); //2. 设置密码 jedis.auth("root"); //3. 选择库 jedis.select(0); }
- 测试
JAVA @Test void testString(){ jedis.set("name","Kyle"); String name = jedis.get("name"); System.out.println("name = " + name); } @Test void testHash(){ jedis.hset("reggie:user:1","name","Jack"); jedis.hset("reggie:user:2","name","Rose"); jedis.hset("reggie:user:1","age","21"); jedis.hset("reggie:user:2","age","18"); Map<String, String> map = jedis.hgetAll("reggie:user:1"); System.out.println(map); }
- 释放资源
JAVA @AfterEach void tearDown(){ if (jedis != null){ jedis.close(); } }
连接池
Jedis
本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式。- 新建一个
com.blog.util
,用于存放我们编写的工具类 - 但后面我们使用
SpringDataRedis
的时候,可以直接在yml
配置文件里配置这些内容
JAVA
public class JedisConnectionFactory {
private static JedisPool jedisPool;
static {
// 配置连接池
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(8);
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(0);
poolConfig.setMaxWaitMillis(1000);
// 创建连接池对象,参数:连接池配置、服务端ip、服务端端口、超时时间、密码
jedisPool = new JedisPool(poolConfig, "101.42.225.160", 6379, 1000, "root");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
}
- 之后我们的测试类就可以修改为如下
JAVA
@SpringBootTest
class RedisTestApplicationTests {
private Jedis jedis = JedisConnectionFactory.getJedis();
@Test
void testString(){
jedis.set("name","Kyle");
String name = jedis.get("name");
System.out.println("name = " + name);
}
@Test
void testHash(){
jedis.hset("reggie:user:1","name","Jack");
jedis.hset("reggie:user:2","name","Rose");
jedis.hset("reggie:user:3","name","Kyle");
jedis.hset("reggie:user:1","age","21");
jedis.hset("reggie:user:2","age","18");
jedis.hset("reggie:user:3","age","18");
Map<String, String> map = jedis.hgetAll("reggie:user:1");
System.out.println(map);
}
@AfterEach
void tearDown(){
if (jedis != null){
jedis.close();
}
}
}
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的支持,使用起来非常简单
- 导入依赖,
XML
<!--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>
<!--Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- 配置Redis
YML
spring:
redis:
host: 127.0.0.1
port: 6379
password: root 需要改成自己的密码
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 100ms
- 自定义序列化
- RedisTemplate可以接收任意Object作为值写入Redis
- 只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的
\xAC\xED\x00\x05t\x00\x06\xE5\xBC\xA0\xE4\xB8\x89
- 缺点:
- 可读性差
- 内存占用较大
- 我们可以自定义RedisTemplate的序列化方式,代码如下:
JAVA
@Configuration
public class redisConfig {
/**
* 创建并配置RedisTemplate,用于操作Redis数据库。
*
* @param factory Redis连接工厂,用于创建Redis连接。
* @return 配置好的RedisTemplate对象,可以用于执行Redis操作。
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 设置连接工厂
template.setConnectionFactory(factory);
// 设置键的序列化方式为字符串序列化
template.setKeySerializer(new StringRedisSerializer());
// 设置哈希键的序列化方式为字符串序列化
template.setHashKeySerializer(new StringRedisSerializer());
// 设置哈希值和值的序列化方式为JSON序列化
template.setHashValueSerializer(serializer());
template.setValueSerializer(serializer());
return template;
}
/**
* 创建并配置JSON序列化器。
*
* @return 配置好的Jackson2JsonRedisSerializer对象,用于将对象序列化为JSON格式。
*/
public Jackson2JsonRedisSerializer<Object> serializer() {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper object = new ObjectMapper();
// 启用对未知类型的默认推断
object.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// 设置在反序列化时,对于未知的属性不抛出异常
object.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
object.registerModule(new JavaTimeModule());
// 设置所有属性可见
object.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 配置ObjectMapper到序列化器
serializer.setObjectMapper(object);
return serializer;
}
}
- 注入RedisTemplate
因为有了SpringBoot的自动装配,我们可以拿来就用
JAVA
@Autowired
private RedisTemplate redisTemplate;
- 编写测试方法
JAVA
@Test
void stringTest(){
redisTemplate.opsForValue().set("username","David");
String username = (String) redisTemplate.opsForValue().get("username");
System.out.println(username);
}
- 我们编写一个User类,并尝试将其创建的对象存入Redis,看看是什么效果
JAVA
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private Integer age;
}
- 测试
JAVA
@Test
void stringTest(){
redisTemplate.opsForValue().set("userdata",new User("张三",18));
}
- 这里采用了JSON序列化来代替默认的JDK序列化方式。最终结果如下:
JSON
{
"com.kab.entity.User",
"name": "张三",
"age": 18
}
- 整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。