文章目录
- [一. 数据缓存](#一. 数据缓存)
-
- [1.1 **为什么要用缓存**](#1.1 为什么要用缓存)
- [1.2 缓存的实现](#1.2 缓存的实现)
- [1.3 Redis](#1.3 Redis)
- [1.4 Redis 数据结构](#1.4 Redis 数据结构)
- [1.5 实现方式](#1.5 实现方式)
- [1.6 对比](#1.6 对比)
- [1.7 实现定时预热缓存](#1.7 实现定时预热缓存)
-
- [1.7.1 什么是热缓存](#1.7.1 什么是热缓存)
- [1.7.2 什么时候用缓存预热](#1.7.2 什么时候用缓存预热)
- [1.7.3 缓存预热带来的问题](#1.7.3 缓存预热带来的问题)
- [1.7.4 怎么实现缓存预热](#1.7.4 怎么实现缓存预热)
- [1.7.5 预热缓存的注意点](#1.7.5 预热缓存的注意点)
- [1.7.6 缓存预热 - 定时任务](#1.7.6 缓存预热 - 定时任务)
- [二. RedisTemplate和Redisson](#二. RedisTemplate和Redisson)
-
- [2.1 简介](#2.1 简介)
- [2.2 主要区别](#2.2 主要区别)
- [2.3 总结](#2.3 总结)
- [三. 如何使用RedisTemplate](#三. 如何使用RedisTemplate)
-
- [3.1 导入依赖](#3.1 导入依赖)
- [3.2 配置application.yml](#3.2 配置application.yml)
- [3.3 配置序列化](#3.3 配置序列化)
- [3.4 测试](#3.4 测试)
- [四. 如何使用Redisson](#四. 如何使用Redisson)
-
- [4.1 导入依赖](#4.1 导入依赖)
- [4.2 配置application.yml](#4.2 配置application.yml)
- [4.3 配置Redisson](#4.3 配置Redisson)
- [4.4 测试](#4.4 测试)
一. 数据缓存
1.1 为什么要用缓存
用户数量很大,导致数据查询慢,导致页面加载的时间过长。
用缓存:提前把数据取出来保存好(通常保存到读写更快的介质,比如内存),就可以更快地读写。
1.2 缓存的实现
●Redis(分布式缓存)
●memcached(分布式)
●Etcd(云原生架构的一个分布式存储,存储配置,扩容能力)
●ehcache(单机)
● 本地缓存(Java 内存 Map)
● Caffeine(Java 内存缓存,高性能)
● Google Guava
单机缓存存在的问题:
- 得到的数据不一致
- 获取不到资源
分布式缓存:
- 用户访问不同的服务器能获得到相同的资源数据
- 也就是在一个公共的服务器上存储数据,其他的服务器都讲数据读写在这个服务器上
redis 内存不能无限增加,一定要设置过期时间!!!
1.3 Redis
NoSQL 数据库
key - value 存储系统(区别于 MySQL,它存储的是键值对)
不是存那种表格之类的数据,不像数据库存储一行一行的数据,而是存储键值对的数据。
1.4 Redis 数据结构
String 字符串类型: name: "yupi"
List 列表:names: ["yupi", "dogyupi", "yupi"]
Set 集合:names: ["yupi", "dogyupi"](值不能重复)
Hash 哈希:nameAge: { "yupi": 1, "dogyupi": 2 }
Zset 集合:names: { yupi - 9, dogyupi - 12 }(适合做排行榜)
bloomfilter(布隆过滤器,主要从大量的数据中快速过滤值,比如邮件黑名单拦截)
geo(计算地理位置)
hyperloglog(pv / uv)
pub / sub(发布订阅,类似消息队列)
BitMap (1001010101010101010101010101)二进制数据
1.5 实现方式
●Spring-Data-Redis**(推荐)**
Spring Data:通用的数据访问框架,定义了一组 增删改查 的接口。
●Spring Data:通用的数据访问框架,定义了一组 增删改查 的接口mysql、redis、jpa
●Jedis: (独立于 Spring 操作 Redis 的 Java 客户端,要配合 Jedis Pool 使用)
●Lettuce
○高阶 的操作 Redis 的 Java 客户端
○异步、连接池
●Redisson:分布式操作 Redis 的 Java 客户端,让你像在使用本地的集合一样操作 Redis(分布式 Redis 数据网格)
1.6 对比
- 如果你用的是 Spring,并且没有过多的定制化要求,可以用 Spring Data Redis,最方便
- 如果你用的不是 SPring,并且追求简单,并且没有过高的性能要求,可以用 Jedis + Jedis Pool
- 如果你的项目不是 Spring,并且追求高性能、高定制化,可以用 Lettuce,支持异步、连接池
- 如果你的项目是分布式的,需要用到一些分布式的特性(比如分布式锁、分布式集合),推荐用 redisson
1.7 实现定时预热缓存
1.7.1 什么是热缓存
●数据量大时,可以提前将数据提前写入缓存,提高第一次访问的数据
●解决第一次访问时间过长,太长时间加载不出来页面,但是后面的访问页面就不会存在访问页面时间过长
●提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
1.7.2 什么时候用缓存预热
-
第一次访问时间比较长,但是后面的访问时间就很快一点。
-
第一次进入系统的加载时间过长
1.7.3 缓存预热带来的问题
- 增加开发成本(你要额外的开发、设计)
- 预热的时机和时间如果错了,有可能你缓存的数据不对或者太老
- 需要占用额外空间
1.7.4 怎么实现缓存预热
- 直接写个缓存刷新页面,上线时手工操作下。
- 数据量不大,可以在项目启动的时候自动进行加载。
- 定时任务,定时刷新缓存
1.7.5 预热缓存的注意点
-
缓存预热的意义(新增少、总用户多)
-
缓存的空间不能太大,要预留给其他缓存空间
-
缓存数据的周期(此处每天一次)
1.7.6 缓存预热 - 定时任务
- 在启动类上加注解
java
@EnableScheduling
public class UserCenterApplication {
public static void main(String[] args) {
SpringApplication.run(UserCenterApplication.class, args);
}
}
-
在方法上添加注解
// cron 秒 分 时 日 月 年(每天这一刻执行)
@Scheduled(cron = "0 26 22 * * *")
public void doPreCacheRecommendUser() {}
二. RedisTemplate和Redisson
2.1 简介
RedisTemplate和Redisson都是操作Redis的Java客户端库。
2.2 主要区别
RedisTemplate:
RedisTemplate则提供了一套更简洁和易用的API,方便开发人员进行数据读写操作,但相对于Redisson,其功能相对较少,如不支持分布式锁等。
RedisTemplate采用了Spring的整体架构,与Spring的各种组件相结合,因此在性能和可靠性上可能会有一些差异。
RedisTemplate作为Spring框架的一部分,与Spring集成度较高,使用起来更加方便。
RedisTemplate适用于基本的Redis操作场景,如缓存、消息队列等。
RedisTemplate采用了传统的同步编程模型,适合于传统的Java应用。
RedisTemplate将Redis的操作封装为一组方法,并提供了丰富的数据类型支持。
Redisson:
Redisson提供了更丰富和更高级的功能和API,比如分布式锁、分布式集合、分布式对象等。它的API设计更加灵活和面向对象,能够更方便地进行并发控制和数据共享。
Redisson基于Netty框架,它在连接池、线程池等方面进行了优化,以提高性能和可靠性。
Redisson则需要额外配置和引入依赖,使用起来相对繁琐一些。
Redisson更适用于需要高级Redis功能的场景,如分布式锁、分布式计数器等。
Redisson采用了Reactive编程模型,支持异步、响应式的操作方式,适合于高并发、高性能的应用。
Redisson将Redis的操作封装为一组对象,并提供了对应的操作方法,更加直观易用。
2.3 总结
RedisTemplate更适合于简单的Redis操作,比如简单缓存、简单消息队列;而Redisson则提供了更高级的功能,比如分布式系统、集群和哨兵模式、RxJava集成。
三. 如何使用RedisTemplate
3.1 导入依赖
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.6.4</version>
</dependency>
3.2 配置application.yml
java
spring:
# redis 配置
redis:
port: 6379
host: localhost
database: 0
3.3 配置序列化
使用 Redis 缓存高频访问用户信息时提到了自定义序列化器,为什么需要自定义序列化器,以及自定义序列化器的实现方式?
由于 Spring Boot Data Redis 默认使用 JDK 序列化器,会将存储到 Redis 的键值对转化为字节数组,不利于在 Redis 可视化工具中阅读、并且不利于跨语言兼容,所以需要指定序列化器。
所以我通过新建 RedisTemplateConfig 配置类来创建自定义的 RedisTemplate Bean,并且通过 redisTemplate.setKeySerializer(RedisSerializer.string()) 指定了 Redis Key 的序列化方式。
java自带的redistemplate只能查询字符串类型
是的,Java自带的RedisTemplate
默认只支持序列化字符串类型。这是因为RedisTemplate
是基于Spring框架的,而Spring Data Redis为了简化操作,默认使用了StringRedisSerializer
来序列化和反序列化数据。这意味着当你使用RedisTemplate
存储非字符串类型的数据时,你需要自定义序列化器来处理这些数据。
java
/**
*
* 自定义序列化
*
*/
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
//创建RedisTemplate对象
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
//设置Key的序列化
redisTemplate.setKeySerializer(RedisSerializer.string());
//创建Json序列化工具
GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//设置Value的序列化
redisTemplate.setValueSerializer(jsonRedisSerializer);
return redisTemplate;
}
}
3.4 测试
在测试包里创建测试类RedisTest,测试代码如下
java
@SpringBootTest
public class RedisTest {
//
@Resource
private RedisTemplate redisTemplate;
@Test
void test(){
ValueOperations valueOperations = redisTemplate.opsForValue();
//增
valueOperations.set("yupiString","dog");
valueOperations.set("yupiInt",1);
valueOperations.set("yupiDouble",2.0);
User user = new User();
user.setId(1L);
user.setUsername("yupi");
valueOperations.set("yupiUser",user);
//查
Object yupi = valueOperations.get("yupiString");
Assertions.assertTrue("dog".equals((String)yupi));
yupi = valueOperations.get("yupiInt");
Assertions.assertTrue(1==((Integer)yupi));
yupi = valueOperations.get("yupiDouble");
Assertions.assertTrue(2.0==((Double)yupi));
System.out.println(valueOperations.get("yupiUser"));
// valueOperations.set("yupiString","dog");
// redisTemplate.delete("yupiString");
}
}
四. 如何使用Redisson
4.1 导入依赖
java
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.19.1</version>
</dependency>
4.2 配置application.yml
java
spring:
# redis 配置
redis:
port: 6379
host: localhost
database: 0
password: 123456
4.3 配置Redisson
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedissonConfig {
private Integer database;
private String host;
private String password;
private String port;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useReplicatedServers()
.setDatabase(database)
.setPassword("".equals(password) ? null : password) // 这样没有密码就用默认值 null 有密码就用密码更加灵活
.addNodeAddress(String.format("redis://%s:%s", host, port));
return Redisson.create(config);
}
}
4.4 测试
编写测试类来使用Redisson 实现分布式锁
@SpringBootTest
public class RedissonTest {
@Resource
private RedissonClient redissonClient;
@Test
void test() {
// list,数据存在本地 JVM 内存中
List<String> list = new ArrayList<>();
list.add("yupi");
System.out.println("list:" + list.get(0));
list.remove(0);
// 数据存在 redis 的内存中
RList<String> rList = redissonClient.getList("test-list");
rList.add("yupi");
System.out.println("rlist:" + rList.get(0));
rList.remove(0);
// map
Map<String, Integer> map = new HashMap<>();
map.put("yupi", 10);
map.get("yupi");
RMap<Object, Object> map1 = redissonClient.getMap("test-map");
// set
// stack
}
}