1、SpringBoot整合Redis
1.1引入Redis依赖
<!-- Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Redis连接池的依赖-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
1.2配置Redis连接信息
spring:
redis: # Redis配置
host: 119.23.106.219
port: 6379
password: 123456
lettuce: # springboot默认引入的是lettuce连接池,如果要是用jedis连接池需要引入jedis依赖
pool:
max-active: 8 # 最大连接
max-idle: 8 # 最大空闲连接
min-idle: 0 # 最小空闲连接
max-wait: 1000ms # 连接等待时间
1.3编写Redis测试类
@SpringBootTest
class BlogApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void testString() {
redisTemplate.opsForValue().set("ycl","杨存乐");
Object name = redisTemplate.opsForValue().get("ycl");
System.out.println(name);
}
}
但是用Redis连接工具查看,看到的是序列化的
点开源码看一下
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
1.4给RedisTemplate的key和values设置序列化
@Configuration
public class RedisConfig {
/**
* 给RedisTemplate的key和values设置序列化
* @param connectionFactory
* @return
*/
@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;
}
}
1.5测试序列化是否成功
1.6测试存入一个对象
/**
* 测试存入一个对象
*/
@Test
void testUser() {
User user = new User();
user.setUsername("杨存乐");
user.setEmail("357487967@qq,com");
redisTemplate.opsForValue().set("user:ycl",user);
User user1 = (User) redisTemplate.opsForValue().get("user:ycl");
System.out.println(user1.toString());
}
1.7使用StringRedisTemplate操作数据
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 测试存入一个对象用StringRedisTemplate操作
*/
@Test
void testUser2() {
User user = new User();
user.setUsername("杨存乐");
user.setEmail("357487967@qq,com");
String userString = JSON.toJSONString(user);
stringRedisTemplate.opsForValue().set("user:str:ycl", userString);
String s = stringRedisTemplate.opsForValue().get("user:str:ycl");
User user1 = JSON.parseObject(userString, User.class);
System.out.println(user1);
}
引入fastJSON依赖,用fastJSON手动序列化JSON
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.71</version>
</dependency>
2、缓存穿透
缓存穿透的原因:是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库,给数据库带来巨大的压力。
常见的两种解决方案:缓存空对象 布隆过滤
2.1缓存空对象
先查缓存,缓存没有查数据库,数据库再没有,缓存一个null对象(避免请求一直访问数据库),并设置一个过期时间(避免无用数据过多的占用Redis内存)
优点:实现简单,维护方便
缺点:额外的内存消耗可能造成短期的不一致
2.2布隆过滤
优点:内存占用较少,没有多余key
缺点:实现复杂存在误判可能
3、缓存雪崩
缓存雪崩的原因:在同一时间大量缓存的key失效或者Redis服务宕机,导致大量请求到达数据库,给数据库带来巨大的压力。
解决方案:
3.1给不同的key的过期时间设置随机值(避免在同一时间大量key过期)
3.2Redis做集群(防止Redis宕机)
3.3给缓存业务添加降级或限流策略
3.4给业务增加多级缓存
4、缓存击穿
缓存击穿的原因:一个被高频访问并且缓存重建业务比较复杂的key突然失效,无数的请求访问会在瞬间给数据库带来巨大的压力
常见的解决方案:互斥锁、逻辑过期
5、博客添加缓存
@Override
@Transactional
public Blog queryBlog(Integer id) {
// 先从Redis中查询
Blog blog = redisUtil.getObject(RedisKeyConstants.Blog_INFO_KEY + id, Blog.class);
if (ObjectUtil.isNotEmpty(blog)) {
return blog;
}
// 从数据库查
blog = getById(id);
if (ObjectUtil.isNotEmpty(blog)) {
// 写入Redis
redisUtil.set(RedisKeyConstants.Blog_INFO_KEY + id, blog);
}
return blog;
}
6、缓存更新策略
在项目中我们采用主动更新策略
主动更新又分为直接更新缓存和直接删除缓存
6.1直接更新缓存
好处就是下次查到的是最新的数据
缺点就是每次更新都有对缓存的操作,如果两次更新中没人访问,对缓存的无效写操作较多
6.2直接删除缓存
6.2.1先删除缓存,再更新数据库
由于操作缓存比更新数据库快,如果在更新的过程中,遇到查询缓存的线程,就会导致写入缓存的是更新前的数据,这样会导致数据库和缓存的数据不一致。
6.2.2先更新数据库,再删除缓存
由于写缓存比更新数据库要快得多,这种出现异常的情况非常少,所以采用这种。
@Override
@Transactional
public Boolean updateBlog(Blog blog) {
boolean b = updateById(blog);
// 判断缓存key是否存在
if (redisUtil.exists(RedisKeyConstants.Blog_INFO_KEY + blog.getId())) {
// 若存在,删除缓存
redisUtil.deleteKey(RedisKeyConstants.Blog_INFO_KEY + blog.getId());
}
return b;
}
6.3延时双删
大概就是这么个意思,理解就行。