ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## redis的面试题地址: 2W字!详解20道Redis经典面试题: https://mp.weixin.qq.com/s/fBShKZbuR54yaIzzMR3R7g redis的数据结构: https://mp.weixin.qq.com/s/MGcOl1kGuKdA7om0Ahz5IA Redis 那点破事 | 绝杀面试官 25 问!https://mp.weixin.qq.com/s/_54mD0EiS9R3CvpDXzN0lg 面试被问Redis的持久化,和面试官大战几个小时 https://mp.weixin.qq.com/s/MbllU3B7wDU8_VTm48-QaA ## redis为什么那么快? 1、基于纯内存操作 2、epoll io多路复用 3、基于hash表,hash对于精准查询的效率很高,用的rehash 算法避免了哈希碰撞 ## 缓存穿透、缓存雪崩、缓存击穿以及怎么解决? **缓存穿透** 就是客户持续向服务器发起对不存在服务器中数据的请求。客户先在Redis中查询,查询不到后去数据库中查询。 1.采用布隆过滤器BloomFilter 2.缓存空值 **缓存击穿** 就是一个很热门的数据,突然失效,大量请求到服务器数据库中 1. 互斥锁,并行请求改为串行 2. 热点数据永不过期 **缓存雪崩** 就是大量数据同一时间失效。 1.双层缓存策略 C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期 2.不同过期时间 设置不同的过期时间,让缓存失效的时间点尽量均匀 3.热点数据永不过期 部分用户访问特别频繁的热点数据,设置永不过期 ## RDB和AOF机制?(如何选择?reb触发机制、aof运行机制?优缺点?) * RDB:在指定的时间间隔能对你的数据进行快照存储。 * AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据 ### 持久化方案选择 #### RDB和AOF的优缺点 RDB和AOF各有优缺点: **RDB持久化** * 优点:RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小。 * 缺点:RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。 **AOF持久化** * 与RDB持久化相对应,AOF的优点在于支持秒级持久化、兼容性好,缺点是文件大、恢复速度慢、对性能影响大。 ** RDB持久化** ### 1. 工作原理: * Redis调用fork(),产生一个子进程。 * 子进程把数据写到一个临时的RDB文件。 * 当子进程写完新的RDB文件后,把旧的RDB文件替换掉。 ### 2\. 触发机制 RDB触发持久化分为手动触发和自动触发 1. save 命令(手动触发) 当客户端向Redis server发送save命令请求进行持久化时,由于Redis是用一个主线程来处理所有,save命令会阻塞Redis server处理其他客户端的请求,直到数据同步完成。save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求,因此线上环境不推荐使用 2. bgsave命令(手动触发) 与save命令不同,bgsave是异步执行的,当执行bgsave命令之后,Redis主进程会fork 一个子进程将数据保存到rdb文件中,同步完数据之后,对原有文件进行替换,然后通知主进程表示同步完成。 3. 自动触发 除了手动触发RDB持久化,Redis内部还存在自动触发机制, 在配置中集中配置 save m n 的方式,表示 m秒内数据集存在n次修改时,系统自动触发bgsave 操作。 ![](https://img.kancloud.cn/82/3e/823ec14458d462da13c5585e1e874827_633x548.png) 1)  Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(后面会详细介绍该命令)的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof 的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。 2)  父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令 3)  父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令 4)  子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换 5)  子进程发送信号给父进程表示完成,父进程更新统计信息 ### 5. 数据恢复 & Redis启动加载数据 RDB文件的载入工作是在服务器启动时自动执行的,并没有专门的命令。但是由于AOF的优先级更高,因此当AOF开启时,Redis会优先载入AOF文件来恢复数据; 只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。服务器载入RDB文件期间处于阻塞状态,直到载入完成为止。 所以Redis的内存数据如果很大,会导致数据恢复时间比较长,因此线上实践更倾向于限制单个Redis的内存不能太大,同时结合Redis Cluster集群使用多节点部署 ## AOF持久化 ### 1.工作原理 由于需要记录Redis的每条写命令,因此AOF不需要触发, AOF的执行流程包括: * 命令追加(append):将Redis的写命令追加到缓冲区aof\_buf; * 文件写入(write)和文件同步(sync):根据不同的同步策略将aof\_buf中的内容同步到硬盘; * 文件重写(rewrite):定期重写AOF文件,达到压缩的目的。 ![](https://img.kancloud.cn/00/ce/00ce171ddc52cb69b6f06750f9636acd_588x317.png) ### 2. AOF 持久化配置 ![](https://img.kancloud.cn/24/ab/24ab1e43be4e703ed9fe5ffc49da366a_427x393.png) ### 3. AOF同步策略 同步步骤分为两步: * Redis收到写命令后首先会追加到AOF缓冲区aof\_buf,而不是直接写入文件系统,因为AOF缓冲区是内存提存的,写入速度极高,可以避免每次写入命令到硬盘,导致硬盘IO成为Redis的负载瓶颈 * 通过调用系统函数 fsync() 把AOF缓冲区的数据真正写到磁盘里面持久化。由于数据是先存储在缓冲区内存里面,如果碰到断电,宕机那么缓冲区里面的数据没来得急落盘就会丢失,因此我们必须有一个相对可靠的机制保证数据落盘。 Redis写命令写入磁盘的命令是通过appendfsync来配置的。 appendfsync 三个取值代表三种落盘策略: * `always`:命令写入aof缓冲区后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈。 * `no`:命令写入aof缓冲区后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证。 * `everysec`:命令写入aof缓冲区后调用系统write操作,write完成后线程返回;fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置。 ### 4\. AOF文件重写(rewrite) 随着写操作的不断增加,AOF文件会越来越大。例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,但是AOF文件里却会把这100次操作完整的记录下来。而事实上要恢复这个记录,只需要1个命令就行了,也就是说AOF文件里那100条命令其实可以精简为1条。所以Redis支持这样一个功能:在不中断服务的情况下在后台重建AOF文件。 ![](https://img.kancloud.cn/98/84/988402d3e6e46d6b5ac9bae986b1665e_828x532.png) 关于文件重写的流程,有两点需要特别注意: (1)重写由父进程fork子进程进行; (2)重写期间Redis执行的写命令,需要追加到新的AOF文件中,为此Redis引入了aof\_rewrite\_buf缓存。 对照上图,文件重写的流程如下: 1) Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行。前面曾介绍过,这个主要是基于性能方面的考虑。 2) 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的。 3.1) 父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程,并可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。 3.2) 由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(图中的aof\_rewrite\_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof\_buf和aof\_rewirte\_buf两个缓冲区。 4) 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。 5.1) 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看。 5.2) 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。 5.3) 使用新的AOF文件替换老文件,完成AOF重写。 #### 重写触发: 1. 手动触发:直接调用bgrewriteaof命令,该命令的执行与bgsave有些类似:都是fork子进程进行具体的工作,且都只有在fork时阻塞。 2. 自动触发:通过配置`auto-aof-rewrite-percentage`和`auto-aof-rewrite-min-size`来完成 `auto-aof-rewrite-percentage 100`:Redis会记住自从上一次重写后AOF文件的大小(如果自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。如果当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。 `auto-aof-rewrite-min-size 64mb`:同时需要设置一个文件大小最小值,只有大于这个值文件才会重写,以防文件很小,但是已经达到百分比的情况。 要禁用自动的日志重写功能,我们可以把百分比设置为0: `auto-aof-rewrite-percentage 0`:禁用日志重写功能 ### 5\. 数据恢复 & Redis启动加载数据 前面提到过,当AOF开启时,Redis启动时会优先载入AOF文件来恢复数据; 只有当AOF关闭时,才会载入RDB文件恢复数据。 ## redis事务?(命令是啥,命令作用,存在的意义) Redis 事务的本质是一组命令的集合,就是一次性、顺序性、排他性的执行一个队列中的一系列命令。 没有隔离级别、不保证原子性 **Redis事务的三个阶段:** * 开始事务 * 命令入队 * 执行事务 **watch** key1 key2 ... : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 ) **multi** : 标记一个事务块的开始( queued ) **exec** : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 ) **discard** : 取消事务,放弃事务块中的所有命令 **unwatch** : 取消watch对所有key的监控 ## 主从复制?(开启命令?原理?引出主从不一致的问题) 1. slave 服务启动,slave 会建立和 master 的连接,发送 sync 命令。 2. master 启动一个后台进程将数据库快照保存到 RDB 文件中 > 注意:此时如果生成 RDB 文件过程中存在写数据操作会导致 RDB 文件和当前主 redis 数据不一致,所以此时 master 主进程会开始收集写命令并缓存起来。 3. master 就发送 RDB 文件给 slave 4. slave 将文件保存到磁盘上,然后加载到内存恢复 5. master 把缓存的命令转发给 slave ## 全量复制和部分复制? 全量复制 1、第一次建立连接进行数据同步是全量复制 2、从节点发送 psync {runid} {offset} 时,runid 与当前主节点的 runid 不匹配则进行全量复制 3、从节点所需要同步数据的偏移量 offset 不在**复制积压缓冲区**中,也会进行全量复制 部分复制 正常情况下,offect在复制积压缓冲区的 ## 哨兵原理?脑裂? * 集群监控,即时刻监控着redis的master和slave进程是否是在正常工作。 * 消息通知,就是说当它发现有redis实例有故障的话,就会发送消息给管理员 * 故障自动转移,如果redis master 节点宕机了的话,它就会将请求转到slave 节点上,slave升为master。 * 充当配置中心,如果发生了故障转移,它会通知将master的新地址写在配置中心告诉客户端。 哨兵每秒向主节点和从节点发送ping命令,在规定时间内没有收到pong, #### 1、什么是脑裂 redis的主从模式下脑裂是指因为网络问题,导致redis master节点跟redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到 master 的存在,就会将某一个 slave 节点提升为 master 节点。此时就存在两个不同的 master节点,就像一个大脑分裂成了两个。 集群脑裂问题中,如果客户端还在基于原来的 master 节点继续写入数据,那么新的master 节点将无法同步这些数据,当网络问题解决之后,sentinel 集群将原先的master节点降为 slave 节点,此时再从新的 master 中同步数据,将会造成大量的数据丢失。 #### redis 脑裂的解决方案 min-slaves-to-write 3 min-slaves-max-lag 10 第一个参数表示至少 master 要有1个slave节点,才允许写入 第二个参数表示 slave 连接到 master 的最大延迟时间 ## 集群方案? https://blog.csdn.net/miss1181248983/article/details/90056960 #### 主从模式 一个master可以拥有多个slave,但是一个slave只能对应一个master 缺点: 从上面可以看出,master节点在主从模式中唯一,若master挂掉,则redis无法对外提供写服务。 #### Sentinel模式 sentinel模式是建立在主从模式的基础上,只是增加了哨兵集群 每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个 PING 命令 **机制** * 如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被sentinel标记为主观下线。 * 如果一个master被标记为主观下线,则正在监视这个master的所有sentinel要以每秒一次的频率确认master的确进入了主观下线状态 * 当有足够数量的sentinel(大于等于配置文件指定的值)在指定的时间范围内确认master的确进入了主观下线状态, 则master会被标记为客观下线 * 在一般情况下, 每个sentinel会以每 10 秒一次的频率向它已知的所有master,slave发送 INFO 命令 * 当master被sentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次 * 若没有足够数量的sentinel同意master已经下线,master的客观下线状态就会被移除; * 若master重新向sentinel的 PING 命令返回有效回复,master的主观下线状态就会被移除 ### Cluster模式 将redis配置文件中的`cluster-enable` 打开即可,每个集群中至少需要三个主数据库才能正常运行 1、多个redis节点网络互联,数据共享 2、所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用 3、客户端可以连接任何一个主节点进行读写 ## 过期删除策略?内存淘汰策略?lru和lfu的区别 **定期删除** Redis 默认会每秒进行十次过期扫描(100ms一次)随机删除20个 **惰性删除** 查询时候检查是否过期,没过期则返回 1. **no-eviction**:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。 2. **volatile-ttl**:从已设置过期时间的数据集(server.db\[i\].expires)中挑选将要过期的数据淘汰; 3. **volatile-random**:从已设置过期时间的数据集(server.db\[i\].expires)中任意选择数据淘汰; 4. **allkeys-lru**:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key; 5. **allkeys-random**:从数据集(server.db\[i\].dict)中任意选择数据淘汰; 6. **volatile-lru**:从已设置过期时间的数据集(server.db\[i\].expires)中挑选最近最少使用的数据淘汰; 4.0 版本后增加以下两种: 7. **volatile-lfu**:从已设置过期时间的数据集(server.db\[i\].expires)中挑选最不经常使用的数据淘汰; 8. **allkeys-lfu**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key。 #### LRU和LFU的区别: LRU是最近最少使用页面置换算法(Least Recently Used),也就是首先淘汰最长时间未被使用的页面! LFU是最近最不常用页面置换算法(Least Frequently Used),也就是淘汰一定时期内被访问次数最少的页! 比如,第二种方法的时期T为10分钟,如果每分钟进行一次调页,主存块为3,若所需页面走向为2 1 2 1 2 3 4 注意,当调页面4时会发生缺页中断 若按LRU算法,应换页面1(1页面最久未被使用) 但按LFU算法应换页面3(十分钟内,页面3只使用了一次) 可见LRU关键是看页面最后一次被使用到发生调度的时间长短, 而LFU关键是看一定时间段内页面被使用的频率! https://www.kancloud.cn/sunjianjun/all_content/2210404 ## 数据类型,结构、编码? ## redis采用的I/O事件模型-epoll ## 分布式锁? ## 布隆过滤器原理? ## 跳跃表skiplist的实现原理? skiplist 会从 header 的顶层出发遍历搜索找到第一个比目标元素小的开始降一层,直到降到最底层 字典和hash的实现原理 基本的命令? 各类型的适用场景? ## Redis常见性能问题和解决方案? (1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件 (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3… 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。 https://blog.csdn.net/a519640026/article/details/106264559?spm=1001.2014.3001.5501 面试题 redis、memcached、mongodb 区别及优缺点使用场景? ## MySQL与MongoDB之间最基本的差别是什么? MySQL和MongoDB两者都是免费开源的数据库。MySQL和MongoDB有许多基本差别包括数据的表示(data representation),查询,关系,事务,schema的设计和定义,标准化(normalization),速度和性能。 通过比较MySQL和MongoDB,实际上我们是在比较关系型和非关系型数据库,即数据存储结构不同。 https://blog.csdn.net/Design407/article/details/106691804 mongodb