您的位置:首页 > 数据库 > Redis

redis缓存雪崩、击穿、穿透+常见问题

2020-01-11 13:52 267 查看

redis在互联网界使用如此之广泛,不仅让人赞叹不已,下面博主来和大家一块研究一下redis的雪崩、击穿、穿透等问题。

  1. 直接通过RedisTemplate来使用
@RequestMapping("/cachetest")
public void test() {
redisCacheTemplate.opsForValue().set("userkey", new User(1, "张三", 25));
User user = (User) redisCacheTemplate.opsForValue().get("userkey");
logger.info("当前获取的:{}", user.toString());
}

2.spring cache注解方式

//一般有三种操作方式:
//@Cacheable:在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;没有则调用方法并将方法返回值放进缓存。
//@CachePut:将方法的返回值放到缓存中。
//@CacheEvict:删除缓存中的数据。
@CachePut(value ="user", key = "#user.id")
public User save(User user) {
userMap.put(user.getId(), user);
logger.info("进入save方法,当前存储对象:{}", user.toString());
return user;
}
@CacheEvict(value="user", key = "#id")
public void delete(int id) {
userMap.remove(id);
logger.info("进入delete方法,删除成功");
}
@Cacheable(value = "user", key = "#id")
public User get(Integer id) {
logger.info("进入get方法,当前获取对象:{}", userMap.get(id)==null?null:userMap.get(id).toString());
return userMap.get(id);
}

今天这篇文章主要介绍一下Redis的缓存雪崩、击穿和穿透

缓存雪崩

假设现在我有一个电商系统,我一般会采取springboot的定时任务去刷新缓存,或者查不到之后去更新缓存。那么定时任务刷新就有一个问题。比如:如果首页所有Key的失效时间都是12小时,中午12点刷新的,我零点有个大型促活动大量用户涌入,假设每秒6000个请求,本来缓存可以抗住这些请求,但是缓存中所有Key都失效了,正好处在失效时间。此时6000个/秒的请求全部落在了DB,DB必然扛不住,好的情况是页面报错,500等,次之就是服务器宕机(在校开发的亲身经历),假如该服务器存在重要数据且尚未缓存,那么造成的损失便是不可估量的,网传之前有一家企业经历过此次事件,该企业损失了几千万

缓存穿透

缓存穿透看上去和缓存穿透字面意思差不多,但其实这是两种概念
假设现在有一个不知名黑客A,不断发起标记有id=-1或者id特别大(即不存在的数据)的请求,我们数据库的id都是从1自增的,这样的不断攻击导致数据库压力很大,严重会击垮数据库,大量的请求也回落到DB上。

缓存击穿

接下来是缓存击穿,这个概念其实跟缓存雪崩有点像,但是又有一点不一样,雪崩是因为大面积的缓存失效,打崩了DB
假设我的系统存在一个热点数据,大量并发集中访问这个点,但是当这个Key在失效的瞬间,持续的大并发直接落到了数据库上,就在这个Key的点上击穿了缓存。这就是缓存击穿

解决方案:

1. 缓存雪崩

(1)不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。可以用随机数生成的方式设置缓存失效时间。可以自定义一个annotation(推荐),也可以

setRedis(key, value, time+Math.random()*10000);

(2)通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

2. 缓存穿透

(1)BloomFilter ,这是一个避免缓存穿透的一个利器

布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

(2)在代码中加入判断,在前期写死(不建议,判断id等容易引起缓存穿透的点,给他堵死!)
(3)如果一个查询返回的数据为空,仍把这个空结果缓存。这样请求就落到了Redis上

3. 缓存击穿

(1)其实我们对于这种往往可以不设置过期时间的方式,简单粗暴
上线之后可以写个测试页面手动添加所有的想要缓存的数据,热加载以下。可以是一个空白页等等(个人之拙见)

例如我的个人博客网站其中有一段就是我热加载我的所有数据到Redis中,假如涉及到增删改等操作直接 @CacheEvict(value=“user”, key = “#id”)
然后在加载到Redis中

面试题

redis的数据类型

  1. Strings
  2. Hash
  3. Lists、
  4. Sets、
  5. Sorted sets
结构类型 结构存储方式 结构的读写方式
String 可以是字符串、整数或者浮点数等基本类型 对整个字符串或者字符串的其中一部分执行操对整个字符串或者字符串的其中一部分执行操作
List 一个链表,链表上的每个节点都包含了一个字符串 从链表的两端推入或者弹出元素:根据偏移量对链表进行修剪。读取单个或者多个元素;根据值来查找或者移除元素
Set 据值来查找或者移除元素(unorderedcollection),并且被包含的每个字符事都是独一无的、各不相同 添加、获取、移除单个元素;检查-一个元素是否存在于某个集合中;计算交集、并集、差集;从集合里卖弄随机获取元素
Hash 包含键值对的无序散列表 添加、获取、移除单个键值对:获取所有键值对
sorted sets 字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定 添加、获取、删除单个元素;根据分值范围(range)或者成员来获取元素
Redis是什么

REmote DIctionary Server(Redis) 是一个由Salvatore
Sanfilippo写的key-value存储系统。 Redis是一个开源的使用ANSI
C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets)
和 有序集合(sorted sets)等类型。

Redis为何这么快

  1. 完全基于内存
  2. redis的数据结构是经过单独设计的,对操作非常简单
  3. 采用单线程,避免了不必要的上下文竞争和切换
  4. 使用多路复用IO模型,非阻塞IO
持久化
  1. AOF,对修改的命令都保存到一个文件里
  2. RDB,内存快照,保存到一个dump的文件中(默认方式)
  • 点赞 2
  • 收藏
  • 分享
  • 文章举报
AAAhxz 发布了7 篇原创文章 · 获赞 68 · 访问量 7067 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: