欢迎光临散文网 会员登陆 & 注册

回答上这些Redis面试题,20K稳了

2023-06-28 10:48 作者:Chinajov  | 我要投稿

##### 1、缓存雪崩

1. 举例

* 双十一期间,所有用户一打开淘宝就是进入首页,首页的压力非常大,为了提高并发,将网站首页数据都缓存到redis 里,所有的redis key 失效时间都是3小时。

* 双十一当天大量用户 剁手狂欢,这时候3个小时过去了,redis里首页的key 缓存全部失效,这时候redis里查询不到数据了,只能去 数据库 中查询,造成数据库无法响应 挂掉。用户进不去首页没法剁手了,马爸爸就 不开心了,把这个程序员外派到 非洲了

2. 一句话总结

* 在高并发下,大量缓存key在 同一时间失效,大量请求直接落在数据库上,导致数据库宕机。

3. 解决方案

* 随机设置key失效时间,避免大量key集体失效。

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

* 若是集群部署,可将热点数据均匀分布在不同的Redis库中也能够避免key全部失效问题o

* 不设置过期时间(不推荐)

* 跑定时任务,在缓存失效前刷进新的缓存(不推荐)


##### 2、缓存穿透

1. 举例

* 老哥做了一个网站火了,动了别人的蛋糕,于是开始 疯狂攻击老哥的网站,由于老哥 网络安全方面学艺不精被人钻了空子。

* 某人用脚本疯狂的给老哥发送请求,查询 id = - 的数据,redis并没有这样的数据,这时候就穿透redis,直按打到了 数据库 上。

* 半夜老哥在睡觉并没有察觉,他疯狂攻击老哥一晚上,结果把 数据库 搞挂了,然后老哥的 网站也挂了

2. 一句话总结

* redis级存和 数据库中没有相关数据(例用户直接携带 d<= 的参数不断发起请求),redis中没有这样的数据,无法进行拦截,直接被穿透到 数据库 ,导致数据库压力过大宕机。

3. 解决方案

* 对不存在的数据缓存到redis中,设置key,value值为null(不管是数据未null还是系统bug问题),并设置一个短期过期时间段,避免过期时间过长影响正常用户使用。

* 拉黑该IP地址。

* 对参数进行校验,不合法参数进行拦截

* 布隆过滤器 将所有可能存在的数据哈希到一个足够大的bitmap(位图)中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。


##### 3、缓存击穿

1. 举例

* 双十一马爸爸突发奇想,想拍卖自己穿了20年的老布鞋,并且附带本人签名,程序员将该鞋的信息存到了redis中,设置了3小时过期。寻思3小时够他们抢了吧,但他低估了马爸爸的魅力。

* 该商品引起了一千万人关注,这些人不断的竞拍这双鞋,价格越拍越高,马爸爸乐开了花,竞拍了2小时59 分,马上要拍到一个亿了,突然这双鞋在redis里的key数据 过期了,导致该key的大量请求,都打到了数据库,直接导致数据库挂掉了,服务无法响应。

* 竞拍到此结束,鞋没卖出去,马爸爸又不开心了,把这个程序员也 外派到非洲了.

2. 一句话总结

* 某一个热点key,在不停地扛着高并发,当这个热点key在 失效的一瞬间,持续的高并发访问就 击破缓存直接访问数据库,导致数据库宕机。

3. 解决方案

* 设置热点数据"永不过期"。

* 加上瓦斥锁:上面的现象是多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它

* 其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后将数据放到redis缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存

4. 最后总结

* 雪崩是大面积的key缓存失效; 穿透是redis里不存在这个缓存key; 击穿是redis 某一个热点key突然失效,最终的受害者都是数据库。

5. 思考

* 未雨绸缪: 将redis、MySQL等搭建成高可用的集群,防止单点

* 亡羊补牢: 服务中进行限流 + 降级,防止MySQL被打崩溃。

* 重振旗鼓: Redis 持久化 RDB+AOF,宕机重启,自动从磁盘上加载数据,快速恢复缓存数据


#### 4、单线程的 Redis 为什么这么快?

1. Redis 有多快 官方给出的答案是读写速度 10万/秒,如果说这是在单线程情况下跑出来的成绩,你会不会惊讶?为什么单线程的 Redis 速度这么快? 原因有以下几点

纯内存操作:

* Redis 是完全基于内存的,所以读写效率非常的高,当然 Redis 存在持久化操作,在持久化操作是都是 fork了进程和利用 Linux 系统的页缓存技术来完成,并不

会影响 Redis 的性能。

* 单线程操作:单线程并不是坏事,单线程可以避免了频繁的上下文切换,频繁的上下文切挽也会影响件能的

* 合理高效的数据结构0

* 采用了非阳塞10 多路复用机制: 多路1/0复用模型是利用 select、pol、epoll 可以同时监察多个流的10 事的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 0 事件时,就从阻塞态中唤醒,于是程字就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。


#### 5、说一说 Redis 的数据过期淘汰策略?

1. Redis 中数据过期策略采用定期删除+惰性删除策略。

* 定期删除、惰性删除策略是什么?

+ 定期删除策略: Redis 启用一个定时器定时监视所有的 key,判断key是否过期,过期的话就删除。这种策略可以保证过期的 key 最终都会被删除,但是也存在严重的缺点: 每次都遍历内存中所有的数据,非常消耗 CPU资源,并且当 key 已过期,但是定时器还处于未唤起状态,这段时间内 key 仍然可以用。

+ 情性删除策略:在获取 key 时,先判断 ey 是否过期,如果过期则删除。这种方式存在一个缺点: 如果这个key 一直未被使用,那么它一直在内存中,其实它已经过期了,会浪费大量的空间。I

2. 定期删除+惰性删除策略是如何工作的?

* 这两种策略天然的互补,结合起来之后,定时删除策略就发生了一些改变,不在是每次扫描全部的 key 了,而是随机抽取一部分 key 进行检查,这样就降低了对 CPU 资源的损耗,惰性删除策略互补了为检查到的key,基本上满足了所有要求。

* 但是有时候就是那么的巧,既没有被定时器抽取到,又没有被使用,这些数据又如何从内存中消失? 没关系,还有内存淘汰机制,当内存不够用时,内存淘汰机制就会上场。Redis 内存淘汰机制有以下几种策略:

+ volatile-ru: 从已设置过期时间的数据集 (server.dbl.expires) 中挑选最近最少使用的数据淘汰

+ volatile-t: 从已设置过期时间的数据集 (server.dbl.expires) 中挑选将要过期的数据淘汰。

+ volatile-random: 从已设置过期时间的数据集 (server.db[.expires) 中任意选择数据淘汰·allkeys-ru: 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key (这个是最常用的)

+ allkeys-random: 从数据集 (server.db[i].dict) 中任意选择数据淘汰

+ no-eviction: 禁止驱逐数据,永不过期,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧! (默认值)

+ 4.0版本后增加以下两种:。

- volatile-fu: 从已设置过期时间的数据集(server.db.expires)中挑选最不经常使用的数据淘汰

- allkeys-Ifu: 当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key


#### 6、redis 和 memcached 的区别?

1. 存储方式不同: memcache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小; Redis 有部份存在硬盘上,这样能保证数据的持久性。

2. 数据支持类型: memcache 对数据类型支持相对简单;Redis 有复杂的数据类型

3. 使用底层模型不同: 它们之间底层实现方式,以及与客户端之间通信的应用协议不一样,Redis 自己构建了 vm机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求,

4. value 值大小不同: Redis 最大可以达到1gb; memcache 只有 1mb。


#### 7、redis 的线程模型是怎么样的?

1. redis 内部使用文件事件处理器 fle event handler,这个文件事件处理器是单线程的,所以redis 才叫做单线程的模型。它采用10 多路复用机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进行处理。

2. 文件事件处理器的结构包含 4 个部分

* 多个 socket

* I0 多路复用程序

* 文件事件分派器

* 事件处理器 (连接应答处理器、命令请求处理器、命令回复处理器)多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 0 多路复用程序会监听多人socket,会将 socket 产生的事件放入队列中排队,事件分派器每次从队列中取出一个事件,把该事件交给对应的事件处理器进行处理。


#### 8、为什么是用Redis做缓存,不用MapGuava?

1. 缓存分为本地缓存和分布式缓存。以Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着 vm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。

2. 使用 redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有致性。缺点是需要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。

#### 9、Redis哨兵

1. sentine1 (哨兵)是Redis 高可用的解决方案,可以运行多个Sentinel组成一个哨兵分布式系统

* Sentinel哨兵职责如下:

+ 监控(Monitoring): Sentinel 会不断地定期检查你的主服务器和从服务器是否运作正常

+ 提醒(Notification):当被监控的某个 Redis 服务器出现问题时,Sentinel 可以通过 AP1向管理员或者其他应用程序发送通知。

+ 自动故喷迁移(Automaticfailover): 当一个主服务器 不能正常工作时,Sentinel 会开始一次自动故障迁移操作它会将失效主服务器的其中一个从服务器 升级 为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。

+ 统一的配置管理: 连接者询问sentinel取得主从的地址

#### 10. 如何实现 redis 事务?

1. Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。

2. 在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性 (Atomicity) 、一致性(Consistency) 和隔离性 (lsolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性 (Durability)。

回答上这些Redis面试题,20K稳了的评论 (共 条)

分享到微博请遵守国家法律