❤️五分钟结束面试,发奋图强爆肝一周,再也不怕被问到Redis了(阿里面试官给我的题库)❤️

Posted XiaoLin__Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了❤️五分钟结束面试,发奋图强爆肝一周,再也不怕被问到Redis了(阿里面试官给我的题库)❤️相关的知识,希望对你有一定的参考价值。

Redis简介

    简单来说 Redis 就是一个使用 C 语言开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。Redis 除了做缓存之外,Redis 也经常用来做分布式锁,甚至是消息队列。

    Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。

说一下 Redis 和 Memcached 的区别和共同点

共同点:

  1. 都是基于内存的数据库,一般都用来当做缓存使用。
  2. 都有过期策略。
  3. 两者的性能都非常高。

区别

  1. Redis 支持更丰富的数据类型(支持更复杂的应用场景)。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。

  2. Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。

  3. Redis 有灾难恢复机制。 因为可以把缓存中的数据持久化到磁盘上。

  4. Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。

  5. Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的.

  6. Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。 (Redis 6.0 引入了多线程 IO )

  7. Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。

  8. Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。

缓存数据的处理流程

  1. 如果用户请求的数据在缓存中就直接返回。
  2. 缓存中不存在的话就看数据库中是否存在。
  3. 数据库中存在的话就更新缓存中的数据。
  4. 数据库中不存在的话就返回空数据。

为什么要用 Redis/为什么要用缓存

    从以下两个点去讲

高性能

    假如用户第一次访问数据库中的某些数据的话,这个过程是比较慢,毕竟是从硬盘中读取的。但是,如果说,用户访问的数据属于高频数据并且不会经常改变的话,那么我们就可以很放心地将该用户访问的数据存在缓存中。

    这样可以保证用户下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。不过,要保持数据库和缓存中的数据的一致性。 如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!

高并发

    一般像 mysql 这类的数据库的 QPS(服务器每秒可以执行的查询次数) 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 redis 的情况,redis 集群的话会更高)。

Redis 常见数据结构以及使用场景分析

string

  1. 可以为整形、浮点型和字符串,统称为元素,点赞、计数、粉丝数

list

    有序串列表,粉丝列表、消息队列、慢查询

hash

    一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象、用户信息

set

    无序集合,集合成员是唯一的,这就意味着集合中不能出现重复的数据。共同关注、喜好、好友,标签

sorted set(ZSet)

    有序集合,集合成员是唯一的。需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。

bitmap

    适合需要保存状态信息(比如是否签到、是否登录…)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。

Redis过期策略

定时删除

概述

    redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key
,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!

优点

保证内存被尽快释放

缺点

  1. 若过期key很多,删除这些key会占用很多的CPU时间,在CPU时间紧张的情况下,CPU不能把所有的时间用来做要紧的事儿,还需要去花时间删除这些key。
  2. 定时器的创建耗时,若为每一个设置过期时间的key创建一个定时器(将会有大量的定时器产生),性能影响严重

总结

    用处理器性能换取存储空间 (拿时间换空间)

惰性删除

    key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除,返回null。

优点

    删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的,而且此时的删除是已经到了非做不可的地步。

缺点

    若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露(无用的垃圾占用了大量的内存)

总结

    用存储空间换取处理器性能(拿空间换时间)

定期删除

    如果当前库中没有一个key设置了过期时间,直接执行下一个库的遍历,随机获取一个设置了过期时间的key,检查该key是否过期,如果过期,删除key,判断定期删除操作是否已经达到指定时长,若已经达到,直接退出定期删除。(默认每个库检测20个key)

优点

  1. 通过限制删除操作的时长和频率,来减少删除操作对CPU时间的占用–处理"定时删除"的缺点 2)定期删除过期key–处理"惰性删除"的缺点。
  2. 定期删除过期key–处理"惰性删除"的缺点。

缺点

  1. 在内存友好方面,不如"定时删除"。
  2. 在CPU时间友好方面,不如"惰性删除"。

讲一下个人对I/O多路复用机制 的理解

    “多路”指的是多个网络连接,“复用”指的是复用同一个线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wEKNeZIe-1631457480782)(D:\\学习笔记\\面试题\\Java框架面试\\Redis\\Redis.assets\\image-20201022172324401.png)]

    多个socket请求,socket包括很多种事件类型,有get、push、exit 等等。这些socket准备好, 将其置入队列之中 ,文件事件分派器依次去队列中取,转发到不同的事件处理器中。

内存淘汰机制

简述

    Redis有过期策略,假如你的Redis只能存1G的数据,你一个请求写入2G,而你也没有及时请求key,那么惰性删除就不生效了,Redis占用内存就会越来越高。

Redis可以设置内存大小:

# maxmemory <bytes>
# 设置Redis最大占用内存大小为100
maxmemory 100mb

    超过了这个内存大小,就会触发内存淘汰机制,Redis有一个默认配置,这个是Redis的默认 内存淘汰机制

# maxmemory-policy noeviction

maxmemory-policy一共有8个值,当内存不足时:

  1. noeviction: 不删除,直接返回报错信息。
  2. allkeys-lru:移除最久未使用(使用频率最少)使用的key。推荐使用这种。
  3. volatile-lru:在设置了过期时间key中,移除最久未使用的key。
  4. allkeys-random:随机移除某个key。
  5. volatile-random:在设置了过期时间的key中,随机移除某个key。
  6. volatile-ttl: 在设置了过期时间的key中,移除准备过期的key。
  7. allkeys-lfu:移除最近最少使用的key。
  8. volatile-lfu:在设置了过期时间的key中,移除最近最少使用的key。

LRU和LFU的区别

LRU

    LRU是最近最少使用页面置换算法(Least Recently Used),也就是首先淘汰最长时间未被使用的页面!

比如有数据 1,1,1,2,2,3 此时缓存中已有(1,2) 当3加入的时候,得把前面的1淘汰,变成(3,2)

LFU

    LFU是最近最不常用页面置换算法(Least Frequently Used),也就是淘汰一定时期内被访问次数最少的页!

    比如有数据 1,1,1,2,2,3 缓存中有(1(3次),2(2次)) 当3加入的时候,得把后面的2淘汰,变成(1(3次),3(1次))

Redis重启如何恢复数据呢?

    Redis启动前会先检查AOF文件,不存在才会去加载RDB文件,因为AOF的数据完整性高,最多也就损失1秒的数据。

总结

  1. AOF恢复比较慢;RDB文件小,恢复快。
  2. RDB是数据快照文件,AOF是命令操作的日志文件,追加写。

缓存击穿、穿透、雪崩有什么分别,如何应对

缓存穿透

简述

    缓存穿透是指缓存和数据库中都没有的数据,而用户却不断发起请求,如发起id为-1的数据,这时的用户可能是攻击者,这种攻击会导致数据库压力过大。

解决方案

  1. 接口层增加校验,如用户鉴权校验,id做基础校验,比如 id<=0的直接拦截; 最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
  2. 另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

缓存击穿

简述

    缓存击穿是指缓存中没有但数据库中有的数据,当一个key非常热点(类似于爆款),在不停的扛着高并发,高并发集中对这一个点进行访问。当这个key在失效的瞬间,持续的高并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

解决方案

  1. 设置热点数据永远不过期。
  2. 加互斥锁。

缓存雪崩

简述

    缓存雪崩是指缓存中数据大批量到过期时间,大批量数据同一时间过期,导致请求量全部请求到数据库,造成数据库宕机。

解决办法

  1. 给缓存失效时间,加上一个随机值,避免大量缓存集体失效。
  2. 双缓存:缓存A和B,比如A的失效时间是20分钟,B不失效。比如从A中没读到,就去B中读,然后异步起一个线程同步到A。

Memcache与Redis的区别

  1. Redis 支持更丰富的数据类型(支持更复杂的应用场景)。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
  2. Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。
  3. Redis 有灾难恢复机制。 因为可以把缓存中的数据持久化到磁盘上。
  4. Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。
  5. Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的.
  6. Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。 (Redis 6.0 引入了多线程 IO )
  7. Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。
  8. Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。

Redis 常见性能问题和解决方案

  1. Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件。(因为RDB生成快照是很耗性能的,这样会阻塞master,会造成master假死)。
  2. 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次(这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度,所以要合理设置AOF重写机制)
  3. Master 和 Slave 最好在同一个局域网内(主从复制的速度快和连接的稳定性强 )
  4. 读写分离,把请求压力分散到各个服务。

Redis 没有使用多线程?为什么不使用多线程?

    **Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**不过,Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主处理之外的其他线程来“异步处理”。大体上来说,Redis 6.0 之前主要还是单线程处理。

    Redis6.0 之前 为什么不使用多线程?

  1. 单线程编程容易并且更容易维护;

  2. Redis 的性能瓶颈不再 CPU ,主要在内存和网络;

  3. 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。

Redis的持久化机制

    Redis是基于内存操作的,但它是一个支持持久化的数据库,通过持久化机制就可以把数据同步到硬盘,当Redis重启的时候就把硬盘的书籍加载到内存。 Redis支持两种持久化机制,一种是RDB,另一种是AOF,可以单独使用其中一种或将二者结合使用。

持久化发生了什么

Redis作为一个内存数据库,要做的关于持久化的事情大概有以下几步:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CqcOVBKZ-1631457480785)(Redis.assets/持久化.png)]

  1. 客户端向数据库发送写的命令(数据在客户端的内存中)。
  2. 数据库接收到客户端的写请求(数据在服务端的内存中)。
  3. 数据库调用系统API将数据写入磁盘(数据在内核缓冲区中)。
  4. 操作系统将写缓冲区传输到磁盘控制器(数据在磁盘缓存中)。
  5. 操作系统的磁盘控制器将数据写入实际的物理媒介中(数据在磁盘中)。

RDB持久化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q36NTAU2-1631457480788)(Redis.assets/快照.gif)]

    RDB持久化是将当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),保存的文件后缀是.rdb;当Redis重新启动时,可以读取快照文件恢复数据。

Redis是一个单线程的程序,这意味着,我们不仅仅要响应用户的请求,还需要进行内存快照。而后者要求 Redis 必须进行 IO 操作,这会严重拖累服务器的性能。还有一个重要的问题是,我们在 持久化的同时,内存数据结构还可能在变化,比如一个大型的 hash字典正在持久化,结果一个请求过来把它删除了,可是这才刚持久化结束,咋办?

RDB持久化原理

操作系统的多进程COW(Copy On Write)机制拯救了Redis。Redis在持久化的时候会调用glibc的函数fork出一个子进程,简单来说就是复制了当前进程作为子进程,子进程和父进程会共享内存里面的代码块和数据段。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W7iWy4wg-1631457480792)(Redis.assets/image-20210527154710871.png)]

快照持久化完全交给子进程来处理,父进程则继续处理客户端请求。子进程做数据持久化,它不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须保持服务客户端的请求,然后对数据内存结构进行不间断的修改。

RDB的缺点

快照不是很持久,如果运行Redis的1计算机突然停止运行了,或者被你kill -9的杀死了进程,则写入Redis的最新数据会丢失,所以快照并不是最好的选择。

AOF持久化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eRiEt3F5-1631457480795)(Redis.assets/aof.gif)]

AOF的工作原理

它的工作方式十分简单,每次执行修改内存中的数据集的写操作的时候,都会记录该操作,举个例子,假设AOF日志记录了自Redis实例创建以来所有的修改性指令序列,那么就可以对一个全新的Redis实例顺序执行所有指令,实现重放的效果,来回复Redis当前实力的内存的数据结构的状态。

当Redis收到客户端的修改指令后,会先对参数进行校验然后执行,如果没有任何问题,就立即将该指令存储到AOF的日志中,Redis是先执行指令再将日志存盘。他和MySQL、HBase等存储引擎是先存储日志,再执行。

AOF重写

Redis在长期运行的过程中,AOF的日志会越变越长,如果实例宕机重启,重放整个AOF日志会十分耗时,导致长时间Redis无法对外提供服务没所以我们需要给AOF日志减个肥。

Redis提供了bgrewriteaof用于对AOF日志进行瘦身,其原理就是开辟一个子进程对内存进行遍历,转化为一系列的Redis的操作指令,序列化到一个新的AOF日志文件中。序列化完毕后再将操作期间发生的增量AOF日志追加到这个新的AOF日志文件中,追加完毕后就立即替代旧的AOF日志文件,瘦身完毕!

fsync(刷盘策略)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y2emd5Xb-1631457480796)(Redis.assets/强制开车.gif)]

    AOF还有另外一个方法,就是刷盘策略fsync,相当于将文件内容强制从内核缓存刷到磁盘,但是这种强制的形式是一个十分耗资源的过程,所以需要节制Redis默认每隔一秒进行一次fsync调用,将缓冲器的数据写入到磁盘。 如果磁盘不稳定,fsync也是会耗时的,也会影响性能。

    AOF有三种刷盘模式:

  1. always:把每个写命令都立即同步到aof,很慢,但是很安全
  2. everysec:每秒同步一次,是折中方案**(推荐方案)**
  3. no:redis不处理,交给OS来处理,非常快,但是也最不安全

Redis混合持久化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLBfZSnh-1631457480798)(Redis.assets/融合.gif)]

重启Redis的时候,我们很少使用RDB的方式来恢复内存状态,因为会丢失大量的数据。我们一般使用AOF日志重放,但是重放AOF日志性能对RDB来说要慢很多,这样在Redis实例很大的情况下,需要花费很长的时间。

Redis4.0为了解决这个难题,带来了一个混合持久化,这种全新的持久化选项。将RDB文件的内容和增量的AOF日志文件存在一起,这里的AOF日志不再是全量的日志,而是从持久化开始到持久化结束的这段时间发生的增量AOF文件,通常这部分AOF日志很小,提高了性能。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gI2i8l7a-1631457480799)(Redis.assets/混合持久化.jpg)]

于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的AOF 全量文件重放,重启效率因此大幅得到提升。

以上是关于❤️五分钟结束面试,发奋图强爆肝一周,再也不怕被问到Redis了(阿里面试官给我的题库)❤️的主要内容,如果未能解决你的问题,请参考以下文章

❤️集合很简单?开什么玩笑?肝了一周,全是精华,万字讲解!面试再不怕集合问题了!!!❤️

❤️集合很简单?开什么玩笑?肝了一周,全是精华,万字讲解!面试再不怕集合问题了!!!❤️

☀️知道这些HTTP状态码,再也不怕面试官了。

看完这篇,面试再也不怕被问 Webpack 热更新

爆肝一周——PYTHON 算法基础

❤️爆肝!整理了一周的Spring面试大全含答案,吊打Java面试官建议收藏!❤️