redis面试缓存数据库-Redis系列

Redis面试题整理-字节头条腾讯面试题 (2020最新版含详细

2020-05-23  本文已影响0人  大富帅

原创文章首发于公众号:「码农富哥」,致力于分享后端技术 (高并发架构, 中间件, Linux, TCP/IP, HTTP, MySQL, Redis), 高性能,分布式,微服务等原创干货面试指南

@TOC

概述

什么是Redis

Redis 是一个使用 C 语言写成的,开源的 key-value 数据库。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。目前,Vmware在资助着redis项目的开发和维护。

Redis为什么这么快

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);

2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路 I/O 复用模型,非阻塞 IO;

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

Redis 数据类型

Redis有哪些数据类型?

Redis目前支持5种数据类型,分别是:
String(字符串):

List(列表):

Hash(字典):

Set(集合):

Sorted Set(有序集合):

Redis的zset实现原理及时间复杂度

数据量少的时候使用压缩链表ziplist实现,有序集合使用紧挨在一起的压缩列表节点来保存,第一个节点保存member,第二个保存score。ziplist内的集合元素按score从小到大排序,score较小的排在表头位置。
数据量大的时候使用跳跃列表skiplist和哈希表hash_map结合实现,查找删除插入的时间复杂度都是O(longN)

持久化原理

什么是持久化

Redis 是一种内存数据库,将数据保存在内存中,一旦进程退出,Redis 的数据就会丢失。
为了解决这个问题,Redis 提供了 RDB 和 AOF 两种持久化方案,将内存中的数据保存到磁盘中,避免数据丢失。

持久化方式RDB和AOF底层原理

Redis 提供了不同级别的持久化方式:RDB(默认方式)和AOF

RDB 持久化方式能够在指定的时间间隔能对你的数据进行快照(snapshotting)存储,将内存中的数据不断写入二进制文件中,默认文件dump.rdb,可配置Redis在n秒内如果超过m个key被修改就自动保存快照。(性能高,但是可能会出现数据丢失)

save 900 1 #900秒内如果超过1个key被修改,则发起快照保存。
save 300 10 #300秒内如果超过10个key被修改,则快照保存。

RDB持久化只会周期性的保存数据,在未触发下一次存储时服务宕机,就会丢失增量数据。当数据量较大的情况下,fork子进程这个操作很消耗cpu,可能会发生长达秒级别的阻塞情况。

SAVE是阻塞式持久化,执行命令时Redis主进程把内存数据写入到RDB文件中直到创建完毕,期间Redis不能处理任何命令。

BGSAVE属于非阻塞式持久化,创建一个子进程把内存中数据写入RDB文件里同时主进程处理命令请求。

如图展示了RDB使用save 或者 bgsave 进行fork子进程进行持久化的流程:


在这里插入图片描述

AOF(Append-only file) 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。(类似于MySql的日志方式,记录每次更新的日志)(性能低,但是数据完整)

当开启AOF后,服务端每执行一次写操作就会把该条命令追加到一个单独的AOF缓冲区的末尾,然后把AOF缓冲区的内容写入AOF文件里,由于磁盘缓冲区的存在写入AOF文件之后,并不代表数据已经落盘了,而何时进行文件同步则是根据配置的appendfsync来进行配置:

appendfsync选项:always、everysec和no:
always:服务器在每执行一个事件就把AOF缓冲区的内容强制性的写入硬盘上的AOF文件里,保证了数据持久化的完整性,效率是最慢的但最安全的;

everysec:服务端每隔一秒才会进行一次文件同步把内存缓冲区里的AOF缓存数据真正写入AOF文件里,兼顾了效率和完整性,极端情况服务器宕机只会丢失一秒内对Redis数据库的写操作;

no:表示默认系统的缓存区写入磁盘的机制,不做程序强制,数据安全性和完整性差一些。

RDB和AOF的优缺点和使用场景

RDB优点:

RDB缺点:

AOF 优点:

AOF 缺点:

如何选择RDB和AOF

RDB 和 AOF在数据恢复时的优先级?

数据恢复时 AOF 优先于 RDB, 因为AOF的同步频率相对高,可靠性高

事务

什么是事务?

Redis事务的概念

Redis 事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

Redis事务命令

命令 格式 作用 返回结果
WATCH WATCH key [key ...] 将给出的Keys标记为监测态,作为事务执行的条件 always OK.
UNWATCH UNWATCH 清除事务中Keys的 监测态,如果调用了EXEC or DISCARD,则没有必要再手动调用UNWATCH always OK.
MULTI MULTI 显式开启redis事务,后续commands将排队,等候使用EXEC进行原子执行 always OK.
EXEC EXEC 执行事务中的commands队列,恢复连接状态。如果WATCH在之前被调用,只有监测中的Keys没有被修改,命令才会被执行,否则停止执行 成功: 返回数组 —— 每个元素对应着原子事务中一个 command的返回结果;失败: 返回NULL;
DISCARD DISCARD 清除事务中的commands队列,恢复连接状态。如果WATCH在之前被调用,释放 监测中的Keys always OK.

Redis事务使用方法

Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的

Redis会将一个事务中的所有命令序列化,然后按顺序执行。

以下示例会原子化地递增foo键和bar键的值:


http://ghoulich.xninja.org/wp-content/uploads/sites/2/2016/10/image-01_transaction-example.png

Redis事务中出错会怎样?

事务期间,可能会遇到几种命令错误:

对于第一种错误,客户端会在EXEC调用之前检测, 通过检查排队命令的状态回复,如果命令使用QUEUED进行响应,则它已正确排队;否则Redis将返回错误。

对于第二种错误,服务端会记住在累积命令期间发生的错误,当EXEC命令调用时,将拒绝执行事务,并返回这些错误,同时自动清除命令队列。即使事务中的某些命令执行失败,其他命令仍会被正常执行。(包括出错命令之后的命令)

为什么Redis事务不支持回滚?

事实上Redis命令在事务执行时可能会失败,但仍会继续执行剩余命令而不是Rollback(事务回滚)。如果你使用过关系数据库,这种情况可能会让你感到很奇怪。然而针对这种情况具备很好的解释:

“如果错误就是发生了呢?”这是一个反对Redis观点的争论。然而应该指出的是,通常情况下,回滚并不能挽救编程错误。鉴于没有人能够挽救程序员的错误,并且Redis命令失败所需的错误类型不太可能进入生产环境,所以我们选择了不支持错误回滚(Rollback)这种更简单快捷的方法。

Redis事务支持隔离性吗

Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。

Redis事务其他实现

基于Lua脚本,Redis可以保证脚本内的命令一次性、按顺序地执行,
其同时也不提供事务运行错误的回滚,执行过程中如果部分命令运行错误,剩下的命令还是会继续运行完
基于中间标记变量,通过另外的标记变量来标识事务是否执行完成,读取数据时先读取该标记变量判断是否事务执行完成。但这样会需要额外写代码实现,比较繁琐

乐观锁与悲观锁的区别?

Redis中的管道有什么用

一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应。这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复。这就是管道(pipelining),是一种几十年来广泛使用的技术。例如许多POP3协议已经实现支持这个功能,大大加快了从服务器下载新邮件的过程。

缓存雪崩,缓存击穿,缓存穿透现象及解决方案

缓存雪崩:

缓存穿透:

缓存击穿:

缓存高可用方案

这就是三者的区别,差不多,但又有一些区别。因为缓存雪崩、穿透和击穿,是缓存最大的问题,要么不出现,一旦出现就是致命性的问题
一般避免以上情况发生我们从三个时间段去分析下:

Redis的过期策略和内存淘汰策略

Redis key过期删除策略

使用过Redis的同学应该知道,我们在设置一个key之后,可以指定这个key的过期时间。那么这个key到了过期时间就会立即被删除吗?Redis是如何删除这些过期key的呢?
Redis是使用定期删除 + 惰性删除 两者配合的过期策略。

内存淘汰策略

Redis在使用内存达到某个阈值(通过maxmemory配置)的时候,就会触发内存淘汰机制,选取一些key来删除。内存淘汰有许多策略,下面分别介绍这几种不同的策略。

如何选取合适的策略?比较推荐的是两种lru策略。根据自己的业务需求。如果你使用Redis只是作为缓存,不作为DB持久化,那推荐选择allkeys-lru;如果你使用Redis同时用于缓存和数据持久化,那推荐选择volatile-lru。

缓存淘汰算法: LRU 和 LFU的区别

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

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

线程模型

Redis集群方案

Redis主从复制是什么及作用?

主从复制的架构图:


在这里插入图片描述

主从复制的作用:

Redis主从复制过程及原理

主从复制过程:

主从复制过程如图所示:


在这里插入图片描述

Redis Sentinel 哨兵机制

Redis Sentinel 是 Redis 高可用的实现方案,它是一个管理多个 Redis 实例的工具。
Redis Sentinel 的主要功能包括 主节点存活检测、主从运行情况检测、自动故障转移 (failover)、主从切换。Redis 的 Sentinel 最小配置是 一主一从。
Redis 的 Sentinel 系统可以用来管理多个 Redis 服务器,该系统可以执行以下四个任务:

如图所示,Redis Sentinel 高可用架构 的示意图:


在这里插入图片描述

Redis 哨兵机制如何实现故障自动转移?

sentinel 集群通过主观下线和客观下线判断redis节点是否失效
默认情况下,每个 Sentinel 节点会以每秒一次的频率对Redis 节点和其它的Sentinel 节点发送 PING 命令,并通过节点的 回复 来判断节点是否在线。

当判断某个Redis节点是客观下线后,Sentinel会把master转移到另外的slave节点,让它充当新的master接受请求,从而保证高可用性。

Redis集群的开源方案有哪些?

Twemproxy
Twemproxy 是 twitter 开源的一个 redis 和 memcache 的 中间代理服务器 程序。Twemproxy 作为 代理,可接受来自多个程序的访问,按照 路由规则,转发给后台的各个 Redis 服务器,再原路返回。Twemproxy 存在 单点故障 问题,需要结合 Lvs 和 Keepalived 做 高可用方案。

在这里插入图片描述

Codis
Codis 是一个 分布式 Redis 解决方案,对于上层应用来说,连接 Codis-Proxy 和直接连接 原生的 Redis-Server 没有的区别。Codis 底层会 处理请求的转发,不停机的进行 数据迁移 等工作。Codis 采用了无状态的 代理层,对于 客户端 来说,一切都是透明的。

在这里插入图片描述

Redis Cluster 集群架构

Redis Cluster 实现了一种 混合形式 的 查询路由,但并不是 直接 将请求从一个 Redis 节点 转发 到另一个 Redis 节点,而是在 客户端 的帮助下直接 重定向( redirected)到正确的 Redis 节点。


在这里插入图片描述

数据分区有哪些算法?

分布式数据库 首先要解决把 整个数据集 按照 分区规则 映射到 多个节点 的问题,即把 数据集 划分到 多个节点 上,每个节点负责 整体数据 的一个 子集。


在这里插入图片描述

数据分布通常有 哈希分区 和 顺序分区 两种方式,对比如下:

分区方式 特点 相关产品
哈希分区 离散程度好,数据分布与业务无关,无法顺序访问 Redis Cluster,Cassandra,Dynamo
顺序分区 离散程度易倾斜,数据分布与业务相关,可以顺序访问 BigTable,HBase,Hypertable

节点取余分区
使用特定的数据,如 Redis 的 键 或 用户 ID,再根据 节点数量 N 使用公式:hash(key)% N 计算出 哈希值,用来决定数据 映射 到哪一个节点上。

在这里插入图片描述

一致性哈希分区
一致性哈希 可以很好的解决 稳定性问题,可以将所有的 存储节点 排列在 收尾相接 的 Hash 环上,每个 key 在计算 Hash 后会 顺时针 找到 临接 的 存储节点 存放。而当有节点 加入 或 退出 时,仅影响该节点在 Hash 环上 顺时针相邻 的 后续节点。

在这里插入图片描述

虚拟槽分区
虚拟槽分区 巧妙地使用了 哈希空间,使用 分散度良好 的 哈希函数 把所有数据 映射 到一个 固定范围 的 整数集合 中,整数定义为 槽(slot)。这个范围一般 远远大于 节点数,比如 Redis Cluster 槽范围是 0 ~ 16383。槽 是集群内 数据管理 和 迁移 的 基本单位。采用 大范围槽 的主要目的是为了方便 数据拆分 和 集群扩展。每个节点会负责 一定数量的槽,如图所示:

在这里插入图片描述

当前集群有 5 个节点,每个节点平均大约负责 3276 个 槽。由于采用 高质量 的 哈希算法,每个槽所映射的数据通常比较 均匀,将数据平均划分到 5 个节点进行 数据分区。Redis Cluster 就是采用 虚拟槽分区。

节点1: 包含 0 到 3276 号哈希槽。
节点2:包含 3277 到 6553 号哈希槽。
节点3:包含 6554 到 9830 号哈希槽。
节点4:包含 9831 到 13107 号哈希槽。
节点5:包含 13108 到 16383 号哈希槽。

这种结构很容易 添加 或者 删除 节点。如果 增加 一个节点 6,就需要从节点 1 ~ 5 获得部分 槽 分配到节点 6 上。如果想 移除 节点 1,需要将节点 1 中的 槽 移到节点 2 ~ 5 上,然后将 没有任何槽 的节点 1 从集群中 移除 即可。

由于从一个节点将 哈希槽 移动到另一个节点并不会 停止服务,所以无论 添加删除 或者 改变 某个节点的 哈希槽的数量 都不会造成 集群不可用 的状态.

说说 Redis Cluster 虚拟槽分区?

Redis Cluster 采用 虚拟槽分区,所有的 键 根据 哈希函数 映射到 0~16383 整数槽内,计算公式:slot = CRC16(key)& 16383。每个节点负责维护一部分槽以及槽所映射的 键值数据,如图所示:

在这里插入图片描述
Redis虚拟槽分区的特点:

三主三从的集群使用多少台机器部署比较好?

土豪型: 使用6台机器,每台部署一个Redis节点
经济型:使用3台机器,每台机器部署2个Redis节点,主从混布,即同一组主从节点,分布在不同节点,从而保证高可用!

Redis性能优化

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

总结

原创文章首发于公众号:「码农富哥」,致力于分享后端技术 (高并发架构, 中间件, Linux, TCP/IP, HTTP, MySQL, Redis), 高性能,分布式,微服务等原创干货面试指南

在这里插入图片描述
上一篇 下一篇

猜你喜欢

热点阅读