自学内容网 自学内容网

Redis八股文(一)

目录

1.什么是Redis?

2.Redis和Memcached有什么区别?

3.为什么Redis作为MySQL的缓存?

4.Redis数据类型及其使用场景分别是什么?

5.五种常见数据类型是怎么实现的?

6.Redis是单线程吗?

7.Redis单线程模式是怎样的?

8.Redis采用单线程为什么还这么快?

9.Redis 6.0之前为什么采用单线程?

10.Redis 6.0之后为什么采用多线程?

11.Redis如何持久化?

12.AOF日志如何实现?

13.AOF日志为什么先执行命令,再将该命令写入到文件中?

14.AOF的缺点?

15.AOF的写回策略有哪些?

16.AOF日志过大,会触发什么机制?        

17.重写AOF日志的过程是怎样的?

18.RDB快照是如何实现的?

19.RDB做快照时会阻塞线程吗?

20.为什么会有混合持久化?


1.什么是Redis?

        Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景

2.Redis和Memcached有什么区别?

        1)Redis支持的数据类型多,比如:string、list、set、zset、hash等;Memcached只支持最简单的key-value数据类型。

        2)Redis支持数据持久化,Memcached不支持持久化。

        3)Redis支持发布订阅模型、lua脚本、事务等,Memcached不支持。

        4)Redis原生支持集群模式,Memcached没有原生集群模式,需要靠客户端实现往集群中分片写入数据。

       

3.为什么Redis作为MySQL的缓存?

        1)Redis具备高性能:用户第一次访问的MySQL数据会缓存在Redis中,下次访问时直接访问缓存。

        2)Redis具备高并发:单台设备的Redis的QPS(Query Per Second,每秒钟处理完请求的次数)是MySQL的10倍,直接访问Redis能够承受的请求远大于MySQL。

4.Redis数据类型及其使用场景分别是什么?

        常见的有五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合),随着 Redis 版本的更新,后面又支持了四种数据类型: BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)

数据类型应用场景
string缓存对象、分布式锁等
list消息队列(生产者需自行实现全局消息唯一ID,不能以消费组形式消费数据)等
hash购物车、缓存对象等
set点赞、共同关注等
zset排行榜、电话等
bitmap签到、用户登录状态等
hyperloglog百万级网页UV计数等
GEO存储地理位置信息的场景,滴滴叫车等
stream消息队列(自动生成全局唯一消息ID,支持以消费组形式消费数据)

5.五种常见数据类型是怎么实现的?

        Tips:SDS(简单动态字符串)。SDS相比C语言的原生字符串:

        SDS不仅可以保存文本数据,还可以保存二进制数据,SDS使用len属性的值而不是以空字符串判断字符串结束;

        SDS使用len记录了字符串长度,获取字符串长度的时间复杂度是O(1),而C语言需要O(n);

        Redis的SDS API是安全的,拼接字符串不会发生缓冲区溢出,因为底层会自动扩容。

6.Redis是单线程吗?

        Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。

        但是,Redis不是单线程的,Redis启动的时候,会启动后台线程(BIO):

        Redis 2.6版本之前,会启动处理关闭文件、AOF刷盘两个线程;

        Redis 4.0版本之后新增了一个新的后台进程用来异步释放Redis内存,也就是lazyfree线程。例如执行 unlink key / flushdb async / flushall async 等命令,会把这些删除操作交给后台线程来执行,好处是不会导致 Redis 主线程卡顿。

        后台线程相当于一个消费者,生产者把耗时任务丢到任务队列中,消费者(BIO)不停轮询这个队列,拿出任务就去执行对应的方法即可。

  • BIO_CLOSE_FILE,关闭文件任务队列:当队列有任务后,后台线程会调用 close(fd) ,将文件关闭;
  • BIO_AOF_FSYNC,AOF刷盘任务队列:当 AOF 日志配置成 everysec 选项后,主线程会把 AOF 写日志操作封装成一个任务,也放到队列中。当发现队列有任务后,后台线程会调用 fsync(fd),将 AOF 文件刷盘,
  • BIO_LAZY_FREE,lazy free 任务队列:当队列有任务后,后台线程会 free(obj) 释放对象 / free(dict) 删除数据库所有对象 / free(skiplist) 释放跳表对象;

7.Redis单线程模式是怎样的?

        Redis 6.0版本之前的单线程模式如下:

        Redis初始化时,创建epoll对象,调用socket()、bind()、listen()创建服务端并监听,然后将监听socket加入到epoll()中,同时注册连接事件处理函数。

        Redis初始化后,主线程进入事件循环,主要做以下事情:

        1)首先调用处理发送队列的函数,判断发送队列是否有任务,如果有,就调用write函数将客户端缓存里的数据发送出去,如果没有发送完,就注册写事件处理函数,等待epoll_wait发现可写后处理。

        2)接着调用epoll_wait等待事件到来

                i)连接事件到来,调用连接事件处理函数,调用accept获取已连接的socket -> 将socket加入到epoll中 -> 注册读事件处理函数;

                ii)读事件到来,调用读事件处理函数,调用read处理客户端发来的数据 -> 解析命令 -> 处理命令 -> 将客户端对象添加到发送队列 -> 将执行结果写到发送缓存区等待发送;

                iii)写事件到来,调用写事件处理函数,通过write将客户端缓存区的数据发送出去,如果没有发送完,则会注册写事件处理函数,等待epoll_wait发现可写后处理。

        

8.Redis采用单线程为什么还这么快?

        1)大部分操作都在内存中完成;

        2)单线程模式避免了多线程之间的竞争;        

        3)采用了I/O多路复用处理大量请求。

9.Redis 6.0之前为什么采用单线程?

        单线程可维护性高,多线程会导致一系列问题,如增加系统复杂性、存在线程切换、加锁解锁、死锁造成的性能损耗。

10.Redis 6.0之后为什么采用多线程?

        Redis的主要工作是网络I/O执行命令,随着网络硬件性能的提升,Redis在网络I/O的处理上会出现性能瓶颈。为了提高网络I/O的并行度,所以对网络I/O采用多线程来处理。但是对于命令的执行,还是采用单线程。

        默认情况下,I/O多线程用来处理写数据,不用来处理读数据

        Redis 6.0版本后,默认会额外启动6个线程

  • Redis-server : Redis的主线程,主要负责执行命令;
  • bio_close_file、bio_aof_fsync、bio_lazy_free:三个后台线程,分别异步处理关闭文件任务、AOF刷盘任务、释放内存任务;
  • io_thd_1、io_thd_2、io_thd_3:三个 I/O 线程,io-threads 默认是 4 ,所以会启动 3(4-1)个 I/O 多线程,用来分担 Redis 网络 I/O 的压力

11.Redis如何持久化?

        1)AOF日志:每执行一条写命令,就把该命令以追加的方式写入到一个文件中;

        2)RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘;

        3)混合持久化:AOF和RDB混合的方式。

12.AOF日志如何实现?

        Redis在执行完一条写命令操作后,就将该命令以追加的方式写入一个文件中,Redis重启后,会先读取该文件里的命令,然后逐一执行,恢复数据。

 

13.AOF日志为什么先执行命令,再将该命令写入到文件中?

        1)避免该命令的额外的检查开销:防止语法错误的命令写入到日志文件中,在恢复数据时会出错。

        2)不会阻塞当前写操作的执行

14.AOF的缺点?

        1)数据可能会丢失:执行命令和将命令写入日志是两个过程,可能还没有将命令写入日志文件中,服务器就发生了宕机,写命令没有写入到文件中,造成数据丢失。

        2)可能会阻塞其他操作:因为AOF日志是在主线程中执行的,所以当Redis把日志文件写入磁盘的时候,会阻塞后续的操作。

15.AOF的写回策略有哪些?

        AOF写入日志的过程:

        Always:每次写操作完成后,都将AOF日志数据写入到磁盘。

        Everysec:每次写操作完成后,先将命令写入到AOF的内核缓存区,每隔一秒将缓存区的数据写到磁盘里。

        No:不由Redis控制写回磁盘的时间。每次写操作完成后,先将命令写入到AOF的内核缓存区,由操作系统决定什么时候将数据写到磁盘。

 

16.AOF日志过大,会触发什么机制?        

       当AOF日志文件大小超过设定的阈值后,触发AOF重写机制,来压缩AOF文件。

        AOF重写机制:读取当前数据库中的所有键值对,每个键值对都用一条命令记录到新的AOF文件中,全部键值对记录完成后,会用新的AOF文件替换掉现有的AOF文件。

17.重写AOF日志的过程是怎样的?

        Redis的重写AOF的过程是由后台子进程bgrewriteaof完成的。

        触发重写机制后,会创建子进程bgrewriteaof,该子进程会读取当前数据库中的所有键值对,每个键值对都用一条命令记录到新的AOF文件中。

        在重写过程中,主进程可以正常处理命令。

        Tips:

                i)为什么是子进程来完成重写AOF日志,而不是子线程?

                子进程重写AOF文件,避免阻塞主进程处理命令;

                如果是子线程来处理重写AOF,子线程和主线程共享内存数据,当修改内存数据的时候需要加锁来保证数据安全,降低性能。而使用子进程,虽然父子进程共享内存数据,但是子进程对该共享区域的数据只能可读,当主进程对内存执行写操作时,会进行写时复制,于是父子进程各自拥有独立的数据副本,不需要加锁来保证数据安全。

                ii)如果在重写过程中,主进程执行写命令更改一个已存在的key-value,会触发写时复制,那么该key-value在子进程和主进程中的内存数据不一致了,这时要怎么办?

                为解决这种数据不一致问题,Redis设置了一个AOF重写缓冲区,在bgrewriteaof执行重写AOF的过程中,如果主进程进行了写操作,那么就会将写命令同时写入AOF缓冲区AOF重写缓冲区

                当子进程完成重写工作后,会给主进程发送一个信号,主进程收到信号后,调用一个信号处理函数,该函数的作用是:将AOF重写缓存区中的内容追加到新的AOF文件中,保证数据一致性;新的AOF进行改名,覆盖现有的AOF文件

18.RDB快照是如何实现的?

        记录某一时刻的所有内存数据到RDB文件中,恢复数据时,将RDB文件中的数据写入到内存。

19.RDB做快照时会阻塞线程吗?

        Redis提供save和bgsave来生成RDB文件。

        save:在主线程中执行该命令,生成RDB文件,如果RDB文件太大,会阻塞主线程

        bgsave:会创建一个子进程来生成RDB,不会阻塞主进程

20.为什么会有混合持久化?

        RDB优点是恢复数据快,但是快照的频率太低会丢失大量数据,频率太高会影响性能;AOF文件优点是丢失数据少,但是恢复数据慢。为了集合两者的优点,所以有了混合持久化。

        混合持久化的过程:

        混合持久化工作在AOF日志重写的过程,当开启了混合持久化,重写AOF日志时,fork出来的子进程会先将与主进程共享的数据以RDB的方式写入到新的AOF文件中,然后主线程的操作命令会被记录到重写缓冲区中,重写缓冲区里的增量命令会以AOF方式写入到AOF文件中,写入完成后通知主进程将含有RDB格式和AOF格式的AOF文件替换旧的AOF文件。

        使用了混合持久化,AOF文件里的前半部分是RDB格式的全量数据,后半部分是AOF格式的增量命令

混合持久化的AOF文件内容

原文地址:https://blog.csdn.net/m0_73825482/article/details/140621867

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!