一、前言
在大规模并发访问系统中,如果你的系统用到redis,在面试的时候面试官往往会问你的系统有没有出现雪崩,击穿,穿透这样的场景,然后是怎样解决的。博主也经常反复温习redis的特性,总是被雪崩,击穿,穿透这几个词迷惑,因为这几个词虽然是形象的比喻,但是用的太恐怖了,往往又让人难以记住。
二、三个场景解析
1、缓存雪崩(在同一时刻出现大面积的缓存过期)
由于原有缓存失效,新缓存未到时间 (例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
其实简单一句话,就是所有缓存同一时间失效了,就是雪崩。但是用雪崩了来形容,感觉有点牵强,让人记不住是什么场景。雪崩就是崩塌了 要死人了。和缓存同一时间失效,比喻不上。还不如直接就叫缓存同时失效。
那我们就暂定:缓存同时失效。
谁的系统会会涉及成这个样子? 所以你用的redis很难出现雪崩的现象,除非是你故意干的。
2、缓存击穿(就是大量并发访问一个key时,缓存失效了)
某个 key 非常非常热,访问非常的频繁,高并发访问的情况下,当这个 key在失效(可能expire过期了,也可能LRU淘汰了)的瞬间,大量的请求进来,这时候就击穿了缓存,直接请求到了数据库,一下子来这么多,数据库肯定受不了,这就叫缓存击穿。某个key突然失效,然后这时候高并发来访问这个key,结果缓存里没有,都跑到db了。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
那我们就暂定(并发访问一个失效的key ),简简单单的。 什么击穿,这是暴力比如。
这种场景可能容易遇到,在高并发场景下,但是也只是瞬间,除非你的并发量真的高到无敌,要不然系统即使缓存被穿透,其实也没什么影响,很快就能恢复。
3、缓存穿透
是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。
简简单单,就是查了一个数据库不存在的数据。意思是如果数据库没有的数据被缓存应该做标志,不要去查询数据库了。
总结:redis的三个恐怖的玩意,我们大事化小,小时化无。 就是你的redis设计时,
1.有没有出现大量缓存同时失效的场景。
2.有没有大量的并发同时访问一个失效时间比较短的key,
3.查询缓存的数据数据库里是不是本身就没有。
相信以上傻逼一样的系统也不会出现这三种情况吧?所以你的系统很少出现什么redis雪崩,击穿,穿透。所以面试问到的时候你也不太懂。
三、springboot使用 redis
在Spring Boot中使用Redis,你需要按照以下步骤进行配置和使用:
-
添加依赖:首先,你需要在你的
pom.xml
文件中添加Spring Boot的Redis依赖。你可以选择spring-boot-starter-redis
或者spring-boot-starter-data-redis
。例如:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
-
配置Redis:在
application.properties
或application.yml
文件中添加Redis的配置。你需要指定Redis的数据库索引、服务器地址、密码(如果有的话)和端口。例如:
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.password=your_password # 如果有密码的话
spring.redis.port=6379
-
连接到Redis:在你的Spring Boot应用中,你可以像注入其他Spring Bean一样注入
RedisConnectionFactory
、StringRedisTemplate
或RedisTemplate
实例。例如:
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
这些模板类提供了许多方法来操作Redis,例如opsForValue().set(key, value)
、opsForValue().get(key)
等。
4. 使用Redis:一旦你有了连接到Redis的模板,你就可以开始使用Redis了。例如,你可以存储和获取字符串、列表、集合、哈希等。
// 存储字符串
stringRedisTemplate.opsForValue().set("myKey", "myValue"); // 获取字符串
String value = stringRedisTemplate.opsForValue().get("myKey"); // 存储哈希
Map<String, String> hash = new HashMap<>();
hash.put("field1", "value1");
hash.put("field2", "value2");
stringRedisTemplate.opsForHash().putAll("myHash", hash); // 获取哈希
Map<String, String> result = stringRedisTemplate.opsForHash().entries("myHash");