Redis的Java客户端
Jedis
以Redis命令作为方法名称,学习成本低,简单实用。但是edis实例是线程不安全的,多线程环境下需要基于连接池来使用
lettuce
Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且是线程安全的。支持Redis的哨兵模式、集群模式和管道模式。
Redisson
Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、LockSemaphore、AtomicLong等强大功能。
Spring Data Redis
结合了Jedis+lettuce
Jedis
Jedis的官网地址: https://github.com/redis/jedis,我们先来个快速入门:
1、引入依赖
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
2、建立连接
private Jedis jedis;
@BeforeEach
void setUp() {
//1.建立连接
jedis = new Jedis("192.168.10.102",6379);
//2.设置密码
jedis.auth("hu123456");
//3.选择库
jedis.select(0);
}
3、测试String类型
@Test
void testString() {
//存入数据
String result = jedis.set("name","虎哥");
System.out.println("result = " + result);
//获取数据
String name = jedis.get("name");
System.out.println("name = " + name);
}
4、释放资源
@AfterEach
void tearDown() {
if(jedis != null){
jedis.close();
}
}
Jedis连接池
Jedis本身线程是不安全的,并且频繁的创建和释放资源有性能消耗,推荐使用Jedis线程池去代替线程直连。
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,"192.168.10.102",6379,1000,"hu123456");
}
public static Jedis getJedis(){
return jedisPool.getResource();
}
使用时直接从配置的池子里获取连接
//从配置的池子里获取连接
jedis = JedisConnectionFactory.getJedis();
Spring Data Redis
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的IDKCollection实现
SpringDataRedis快速入门
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
SpringBoot已经提供了对SpringDataRedis的支持,使用非常简单:
1、引入依赖
<!--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>
2、配置文件
spring:
redis:
host: 192.168.10.102
port: 6379
password: hu123456
lettuce:
pool:
max-active: 8 #最大连接
max-idle: 8 #最大空闲连接
min-idle: 0 #最小空闲连接
max-wait: 100ms #连接等待时间
3、注入RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
4、编写测试
@Test
void testString() {
//写入一条String数据
redisTemplate.opsForValue().set("name","虎哥");
Object name = redisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
SpringDataRedis的序列化方式
RedisTemplate可以接收任意Object作为值写入Redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,得到的结果是这样的:
所以,我们可以自定义RedisTemplate的序列化方式,代码如下:
@Configuration
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;
}
}
尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。**
Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:
以下使用StringRedisTemplate类存储不同类型的使用方式:
@Autowired
private StringRedisTemplate stringRedisTemplate;
//存字符串
@Test
void testString() {
//写入一条String数据
stringRedisTemplate.opsForValue().set("name","虎哥");
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
private static final ObjectMapper mapper = new ObjectMapper();
//存对象
@Test
void testSaveUser() {
//创建对象
User user = new User("虎哥", 21);
String jsonUser = null;
try {
//手动序列化
String json = mapper.writeValueAsString(user);
//写入数据
stringRedisTemplate.opsForValue().set("user:200",json);
//获取数据
jsonUser = stringRedisTemplate.opsForValue().get("user:200");
mapper.readValue(jsonUser,User.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println("jsonUser = " + jsonUser);
}
//存Hash
@Test
void testHash() {
stringRedisTemplate.opsForHash().put("user:400","name","虎哥");
stringRedisTemplate.opsForHash().put("user:400","age","21");
Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries("user:400");
System.out.println("stringRedisTemplate = " + entries);
}