转载本文请联系JAVA日知路公众号。小张兴冲冲的去面试,结果被面试官揍了一顿!小张:面试官你好。我是来面试的采访者:你好,小张。看了你的简历,对Redis很精通,所以就问你几个Redis相关的问题。首先我的问题是,Redis是单线程的还是多线程的?小张:不同版本的Redis使用的线程模型是不一样的。Redis4.0之前采用的是单线程模型,4.0之后加入。支持多线程。在4.0之前,虽然我们说Redis是单线程的,但只是说它的网络I/O线程和Set、Get操作都是一个线程完成的。但是Redis的持久化和集群同步还是使用其他线程来完成的。4.0之后加入了多线程支持,主要体现在大数据的异步删除功能上,比如unlinkkey、flushdbasync、flushallasync等。面试官:这个回答很好,那为什么Redis会选择4.0之前用单线程?而且用单线程还这么快?小张:个人认为选择单线程主要是因为好用,没有锁竞争,所有的操作都可以在没有锁的情况下完成,没有死锁和线程切换带来的性能和性能。时间开销,但同时单线程无法充分发挥多核CPU的性能。至于为什么单线程这么快,我觉得主要有以下几个原因:Redis的大部分操作都是在内存中完成的,内存本身的执行效率就非常快,使用了高效的数据结构,比如哈希表和跳过。表面。使用单线程避免了多线程竞争,节省了多线程切换带来的时间和性能开销,不会造成死锁。I/O多路复用机制用于处理大量的客户端Socket请求,因为它是基于非阻塞I/O模型的,这使得Redis可以高效地通过网络进行通信,I/O读写进程不再阻塞。面试官:对,Redis是怎么防止数据丢失的?小张:Redis的数据是存放在内存中的。为了保证Redis的数据不丢失,数据必须从内存存储到磁盘,这样才能存储到服务器上。重启后可以从磁盘恢复原来的数据,这就是Redis的数据持久化。持久化Redis数据的三种方式。AOF日志(AppendOnlyFile,文件追加方式):记录所有的操作命令,并以文本的形式追加到文件中。RDB快照(RedisDataBase):将某一时刻的内存数据以二进制形式写入磁盘。混合持久化方式:Redis4.0增加了混合持久化方式,它综合了RDB和AOF的优点。面试官:那分别说说AOF和RDB的实现原理。小张:AOF采用的是post-writelog的方式。Redis首先执行命令将数据写入内存,然后将日志记录到文件中。AOF日志记录的是操作命令,而不是实际的数据。如果采用AOF方式进行故障恢复,整个日志需要执行一次。RDB采用内存快照的方式,记录某一时刻的数据,而不是操作。所以在使用RDB方式进行故障恢复时,只需要直接将RDB文件读入内存即可实现快速恢复。面试官:您刚才提到AOF采用的是“先写后记录”的方式,而我们平时使用的MySQL采用的是“先记录后记录”的方式,那么为什么Redis会先执行命令,然后再将数据写入到日志中呢?小张:额头开始冒汗了,你问什么问题?..嗯,这主要是因为Redis在写日志之前不会检查命令的语法,所以只记录成功执行的命令,避免记录错误命令的情况,命令执行后再写日志不会阻塞当前的写操作。采访者:那写日记的风险是什么?小张:我……我不知道怎么办。面试官:嗯,写日志后主要有两个风险:数据可能会丢失:如果Redis刚刚执行完命令,此时出现故障,就会有丢失这条命令的风险。可能会阻塞其他操作:AOF日志实际上是在主线程中执行的,所以当Redis将日志文件写入磁盘时,仍然会阻塞后续操作而无法执行。我还有一个问题:RDB做快照的时候线程会不会被阻塞?小张:Redis提供了两条命令来生成RDB快照文件,分别是save和bgsave。save命令在主线程中执行,会造成阻塞。bgsave命令会创建一个写入RDB文件的子进程,避免阻塞主线程,这也是RedisRDB的默认配置。面试官:RDB做快照的时候可以修改数据吗?小张:save是同步的,会阻塞client命令,但是bgsave的时候可以修改。面试官:那么Redis是如何在bgsave做快照的时候允许修改数据的呢?小张:(怎么还问...I?can't!)嗯,这个我不是很了解。。。面试官:这个主要是用bgsave的子线程实现的。具体操作如下:如果主线程执行了read操作,主线程和bgsave的子进程交互没有影响;如果主线程执行写操作,会复制一份修改后的数据,然后bgsave子进程将复制的数据写入到RDB文件中。在这个过程中,主线程仍然可以直接修改原始数据。需要注意的是,Redis对于RDB的执行频率非常重要,因为它会影响快照数据的完整性和Redis的稳定性,所以在Redis4.0之后,增加了AOF和RDB相结合的数据持久化机制:把数据在RDB中写入AOF格式的文件,然后将后续的操作命令以AOF格式保存在文件中,这样既保证了Redis的重启速度,又降低了数据丢失的风险。小张:学习了。面试官:那你说说Redis是怎么实现高可用的?小张:Redis实现高可用主要有三种方式:主从复制、哨兵模式、Redis集群。主从复制是将数据从之前的一台Redis服务器同步到多台Redis从服务器,即主从模式,与MySQL主从复制原理相同。在sentinel模式下使用Redis主从服务时,会出现一个问题,就是当Redis的主从服务器出现故障宕机时,需要手动恢复。为了解决这个问题,Redis增加了哨兵模式(因为哨兵模式可以监控主从服务器,并提供自动容灾功能)。RedisCluster(集群)RedisCluster是一种分布式、去中心化的运行模式。是Redis3.0版本引入的Redis集群方案。它将数据分布在不同的服务器上,减少系统对单个master节点的依赖,从而提高Redis服务的读写性能。面试官:使用哨兵模式,数据有副本数据保障,可用性有哨兵监控。一旦master宕机,slave节点就会被选举为主节点。这已经满足了我们生产环境的需求,为什么还要用呢?集群模式呢?小张:嗯,哨兵模式就是根节点或者说主从模式。在主从模式下,我们可以通过增加从节点来扩展读并发能力,但是没有办法扩展写容量和存储容量。存储容量只能是主节点所能承载的上限。所以为了扩展写入和存储能力,我们需要引入集群模式。面试官:集群中有这么多master节点,redis集群存储时如何确定选择哪个节点?小张:这个应该用到某种哈希算法,但我不确定。..采访者:好了,今天的采访就到这里。回去等我们的面试通知。小张:好的,谢谢面试官,请问redis集群是如何实现节点选择的?面试官:RedisCluster采用了类似的一致性哈希算法来实现节点选择。至于什么是一致性哈希算法自己回去看吧。RedisCluster将自己划分为16384个Slots(槽)。哈希槽类似于数据分区。每个键值对将根据其键映射到一个哈希槽。具体执行过程分为两步。.根据键值对的key,按照CRC16算法计算出一个16位的值。然后用16bit的值对16384取模得到0~16383范围内的模数,每个模数代表一个hashslot对应的数。每个Redis节点负责处理一部分槽。如果有3个主节点ABC,每个节点负责的slot如下:节点处理slotA0-5000B5001-10000C10001-16383这样就实现了集群节点的选择。
