前言
备战面试刷Redis面试题,觉得不能光看还得有输出,于是写文用大白话来回答面试题。
本文仅包括小林coding中Redis面试篇中关于认识Redis、Redis的数据结构、Redis线程模型和Redis持久化部分
写文果然是有助于理解的方式,感觉在倒逼自己思考
参考资料:
一、认识Redis
1. 什么是Redis
Redis 是一种键值对数据库,常应用于分布式的场景,作为 MySQL 的缓存,也可以当作消息队列,以及分布式锁等场景。
2. Redis 和 Mencached 有什么区别
它们相同的点是:都是基于内存的数据库,可以当做缓存,都有过期策略,性能都很高。
主要区别是:Redis 支持更多数据类型,而 Mencached 只支持最简单的 key-value。Redis 支持数据持久化。Redis 原生支持分片集群。Redis 还支持发布订阅模型,Lua 脚本等。
3. 为什么选 Redis 作为 MySQL 的缓存
主要是因为 Redis 具备高性能和高并发。高性能指 Redis 是基于内存模型的,操作速度很快。高并发是指 Redis 单机就可破10w,而 MySQL 单机很难破 1w。
高并发的的主要原因有:基于内存的模型,高效的数据结构,单线程的多路复用模型
二、Redis 的数据结构
1. Redis 的数据类型和使用场景有哪些
Redis 的数据结构有
- string: 主要用于缓存对象,分布式锁,共享 session 信息等
- list:可用作消息队列,但是不支持多个消费者,缺少消息可靠性
- hash:可用作缓存对象,购物车(key为用户id,field为商品id,value为商品数量)
- set:聚合计算场景,比如点赞,共同关注
- zset:排序场景,比如排行榜
以及后续新增的几个特殊类型:
- bitmap:用于二值化的统计,比如签到,判断用户登录,判断连续签到等场景
- hyperloglog:用于大基数的统计需求,比如百万计用户访问量(UV)的统计
- GEO:存储地理位置
- Stream:消息队列,相比 list 支持分组消费,消息确认
2. 五种常见的数据结构是怎么实现的
- string:实现方式是 SDS,可以存放二进制数据,维护一个长度,所以可以用O(1)得到数据长度,会自动扩容,所以不怕缓冲区溢出
- list:旧版双向链表或压缩列表,新版 quicklist
- hash:旧版压缩链表或哈希表,新版 listpack 和哈希表
- set:哈希表和整数集合
- zset:旧版压缩列表或跳表,新版 listpack 和跳表
上述的"或"表示当数据大小到达一定数值时,会使用另一数据结构
三、Redis 线程模型
1. Redis 是单线程的吗
Redis 处理用户请求,解析请求,读写数据,返回数据这一连串操作是由单线程(主线程)完成的。但是 Redis 后台还有其他线程,用于 AOF 刷盘、关闭文件、释放内存
2. Redis 的单线程模型是怎么样的
Redis 在初始化阶段使用 epoll_create 创建一个内核空间的句柄,同时创建服务端,注册连接事件处理函数
Redis 的主线程会进入事件循环,首先处理写发送队列,没有发送完的话注册写事件函数。然后调用 epoll_wait() 等待事件到来。
如果是连接事件,则获取连接,并注册读事件函数;如果是读事件,会将执行结果放到写发送队列;如果是写事件,会继续发送数据,没发送完再继续注册写事件。
3. Redis 采用单线程为什么还这么快
Redis 的大部分操作都在内存中进行;Redis采用单线程模型避免了多线程之间的竞争;I/O多路复用模型使能够一个线程处理多个I/O流
4. Redis 6.0 之前为什么使用单线程
Redis 主要性能瓶颈在于内存大小和网络带宽,增加线程反而增加了切换线程的开销
5. 为什么 Redis 6.0 之后使用多线程
Redis 6.0 之后,在网络传输上使用多线程,提高网络I/O的并行度
四、Redis 持久化
1. Redis 是如何保证数据不丢失的
有三种方式:
- RDB:将某一刻的内存数据以二进制保存
- AOF:记录每次内存操作,在复原时执行这些操作
- 混合持久化:结合 RDB 和 AOF 两种方式的优点,比 AOF 复原更快,比 RDB 更不容易丢数据
2. AOF日志是如何实现的
在客户端发起写操作后,Redis先执行写操作,再将写操作语句写入到AOF日志文件
为什么是执行写操作,再写日志文件
这样有好处就是:不需要进行语法检查,执行失败不会写文件;不会阻塞写操作的执行
也有坏处就是:如果在执行完成和写日志之间宕机了,数据会丢失;写文件操作还是会阻塞后续操作的执行
AOF写回策略有哪些
AOF写回分为几步:写到server.aof_buf缓冲区,系统调用写到内核缓冲区,由操作系统控制写到磁盘
因此写文件操作可以配置不同的策略,比如
Always:每次执行完写操作后将日志写到磁盘,性能开销较大,数据不容易丢失
Everysec:每次执行完写操作会将日志写到内核缓冲区,每秒刷盘,性能开销适中,宕机可能丢失一秒内的数据
No:每次执行完写操作会将日志写到内核缓冲区,由操作系统决定何时刷盘,性能较好,宕机可能丢失更多数据
AOF日志文件过大时,会触发什么机制
AOF日志文件过大时,会触发AOF重写,将当前数据库每一个键值对以一条命令记录到新的AOF文件
AOF重写过程是如何执行的
AOF重写过程由一个子进程执行,可以防止阻塞主进程的执行,同时和主进程共享内存数据,在重写期间,如果主进程对数据做写操作,会触发"写时复制",父子进程就会有独立的数据副本
但是这样会出现父子进程数据不一致,因此在AOF重写过程中,父进程执行写操作后不仅会往写AOF缓冲区,还会写AOF重写缓冲区,在完成重写后,会将AOF重写缓冲区的内容追加到新的AOF文件
3. RDB快照是如何实现的
RDB快照是将某一时刻的内存数据保存下来,然后在宕机的时候可以将数据导入内存进行恢复,执行效率比AOF要高
使用save命令会在主线程生成RDB文件,会造成阻塞,可以使用bgsave使用子进程生成RDB文件,也可以在配置文件配置使用bgsave的条件,如save 3600 1表示3600秒内有一次修改即执行一次bgsave
内存快照这一操作本身是比较耗时的,执行频率太高会对性能有影响,执行频率太低在故障时又可能丢失更多数据
在执行过程中,如果父进程修改数据,会触发"写时复制",两者互不影响,因此bgsave的时候父进程是可以修改数据的
4. 为什么会有混合持久化
由于使用AOF在恢复数据时较慢,使用RDB的话频率又不好把握,为了结合两者的优点,所以使用混合持久化
混合持久化的含义是在AOF重写阶段,将父子进程共享的内存数据以RDB的格式写入到AOF文件,再将AOF重写缓冲区中内容追加到其后,那么该文件前半部分是RDB格式,后半部分是AOF格式
这样的好处是在恢复数据的时候速度比只使用AOF要快,且能保证更少的数据丢失;缺点是混合持久化的AOF文件可读性差,可能在不同版本Redis不兼容