上次的Redis连环问问到了Redis是什么,Redis支持的数据类型和缓存雪崩缓存穿透缓存击穿。
这次来继续问:
Redis为什么这么快?
Redis和memcache的区别?
内存过期策略和淘汰策略?
数据如何持久化?
-
Redis为何这么快?
面试官:redis作为缓存大家都在用,那redis一定很快咯?
我:当然了,官方提供的数据可以达到100000+的QPS(每秒内的查询次数),这个数据不比Memcached差!
面试官:redis这么快,它的“多线程模型”你了解吗?(露出邪魅一笑)
我:您是想问Redis这么快,为什么还是单线程的吧。Redis确实是单进程单线程的模型,因为Redis完全是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章的采用单线程的方案了(毕竟采用多线程会有很多麻烦)。
面试官:嗯,是的。那你能说说Redis是单线程的,为什么还能这么快吗?
我:可以这么说吧。
第一:Redis完全基于内存,绝大部分请求是纯粹的内存操作,非常迅速,数据存在内存中,类似于HashMap,HashMap的优势就是查找的操作的时间复杂度是O(1)。
第二:数据结构简单,对数据操作也简单。
第三:采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU切换,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有死锁问题导致的性能消耗。
第四:使用多路复用非阻塞IO模型。
分析:这个问题其实是对redis内部机制的一个考察。
题外话:我们现在要仔细的说一说I/O多路复用机制,因为这个说法实在是太通俗了,通俗到一般人都不懂是什么意思。
博主打一个比方:小曲在S城开了一家快递店,负责同城快送服务。小曲因为资金限制,雇佣了一批快递员,然后小曲发现资金不够了,只够买一辆车送快递。
经营方式一:
客户每送来一份快递,小曲就让一个快递员盯着,然后快递员开车去送快递。一个快递对应一个快递员。
慢慢的小曲就发现了这种经营方式存在下述问题:
几十个快递员基本上时间都花在了排队等车上了,大部分快递员都处在闲置状态,谁抢到了车,谁就能去送快递。随着快递的增多,快递员也越来越多,小曲发现快递店里越来越挤,没办法雇佣新的快递员了。而且快递员之间的协调很花时间。
综合上述缺点,小曲痛定思痛,提出了下面的经营方式。
经营方式二:
小曲只雇佣一个快递员。然后呢,客户送来的快递,小曲按送达地点标注好,然后依次放在一个地方。最后,那个快递员依次的去取快递,一次拿一个,然后开着车去送快递,送好了就回来拿下一个快递。
对比上述两种经营方式对比,是不是明显觉得第二种,效率更高,更好呢。
当然这只是按redis做的一个比喻,如果谁家开快递真的这样做恐怕造就倒闭了。
在上述比喻中:
每个快递员------------------>每个线程
每个快递-------------------->每个socket(I/O流)
快递的送达地点-------------->socket的不同状态
客户送快递请求-------------->来自客户端的请求
小曲的经营方式-------------->服务端运行的代码
一辆车---------------------->CPU的核数
于是我们有如下结论:
1、经营方式一就是传统的并发模型,每个I/O流(快递)都有一个新的线程(快递员)管理。
2、经营方式二就是I/O多路复用。只有单个线程(一个快递员),通过跟踪每个I/O流的状态(每个快递的送达地点),来管理多个I/O流。
下面类比到真实的redis线程模型,如图所示
参照上图,可知redis-client在操作的时候,会产生具有不同事件类型的socket。在服务端,有一段I/0多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。
需要说明的是,这个I/O多路复用机制,redis还提供了select、epoll、evport、kqueue等多路复用函数库,大家可以自行去了解。
-
Redis和Memcached的区别
面试官:嗯嗯,说的很详细。那你为什么选择Redis的缓存方案而不用memcached呢?
1、存储方式上:memcache会把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。redis有部分数据存在硬盘上,这样能保证数据的持久性。
2、数据支持类型上:memcache对数据类型的支持简单,只支持简单的key-value,,而redis支持五种数据类型。
3、使用底层模型不同:它们之间底层实现方式以及与客户端之间通信的应用协议不一样。redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4、value的大小:redis可以达到512M,而memcache只有1MB。
更详细的区别可以参考我上一篇文章:说说Redis和memcache有什么区别与不同?
-
内存淘汰策略
在内存淘汰之前redis有个内存过期策略。Redis使用的是定期删除+惰性删除策略。
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
面试官:那你说说你知道的redis的淘汰策略有哪些?
我:Redis有六种淘汰策略
策略 | 描述 |
---|---|
volatile-lru | 从已设置过期时间的KV集中优先对最近最少使用(less recently used)的数据淘汰 |
volitile-ttl | 从已设置过期时间的KV集中优先对剩余时间短(time to live)的数据淘汰 |
volitile-random | 从已设置过期时间的KV集中随机选择数据淘汰 |
allkeys-lru | 从所有KV集中优先对最近最少使用(less recently used)的数据淘汰 |
allKeys-random | 从所有KV集中随机选择数据淘汰 |
noeviction | 不淘汰策略,若超过最大内存,返回错误信息,一般没人用。 |
补充一下:Redis4.0加入了LFU(least frequency use)淘汰策略,包括volatile-lfu和allkeys-lfu,通过统计访问频率,将访问频率最少,即最不经常使用的KV淘汰。
-
数据持久化
面试官:你对redis的持久化机制了解吗?能讲一下吗?
我:redis为了保证效率,数据缓存在了内存中,但是会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件中,以保证数据的持久化。
Redis的持久化策略有两种:
1、RDB:快照形式会在指定的时间间隔能对你的数据进行快照存储。
2、AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。
Redis默认是快照RDB的持久化方式。写操作命令记录的格式跟Redis协议一致,以追加的方式进行保存。
当Redis重启的时候,它会优先使用AOF文件来还原数据集,因为AOF文件保存的数据集通常比RDB文件所保存的数据集更完整。
持久化策略可以禁用,两种策略也可以共存。
面试官:那你再说下RDB是怎么工作的?
我:默认Redis是会以快照"RDB"的形式将数据持久化到磁盘的一个二进制文件dump.rdb。工作原理简单说一下:当Redis需要做持久化时,Redis会fork一个子进程,子进程将数据写到磁盘上一个临时RDB文件中。当子进程完成写临时文件后,将原来的RDB替换掉,这样的好处是可以copy-on-write。
RDB的优点是:这种文件非常适合用于备份:比如,你可以在最近的24小时内,每小时备份一次,并且在每个月的每一天也备份一个RDB文件。这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。RDB非常适合灾难恢复。
RDB的缺点是:RDB容易造成数据的丢失。如果你需要尽量避免在服务器故障时丢失数据,那么RDB不合适你。如果数据量比较大的话还有可能导致redis停止服务几毫秒。
面试官:那你要不再说下AOF ?
我:(说就一起说下吧)使用AOF做持久化,每一个写命令都通过write函数追加到appendonly.aof中,配置方式如下:
AOF可以做到全程持久化,只需要在配置中开启 appendonly yes。这样redis每执行一个修改数据的命令,都会把它添加到AOF文件中,当redis重启时,将会读取AOF文件进行重放,恢复到redis关闭前的最后时刻。
我顿了一下,继续说:使用AOF的优点是会让redis变得非常耐久。可以设置不同的fsync策略,aof的默认策略是每秒钟fsync一次,在这种配置下,就算发生故障停机,也最多丢失一秒钟的数据。缺点是对于相同的数据集来说,AOF的文件体积通常要大于RDB文件的体积。根据所使用的fsync策略,AOF的速度可能会慢于RDB。而在禁止fsync的情况下速度可以达到RDB的水平。
面试官又问:你说了这么多,那我该用哪一个呢?
我:如果你非常关心你的数据,但仍然可以承受数分钟内的数据丢失,那么可以额只使用RDB持久。AOF将Redis执行的每一条命令追加到磁盘中,处理巨大的写入会降低Redis的性能,不知道你是否可以接受。
数据库备份和灾难恢复:定时生成RDB快照非常便于进行数据库备份,并且RDB恢复数据集的速度也要比AOF恢复的速度快。当然了,redis支持同时开启RDB和AOF,系统重启后,redis会优先使用AOF来恢复数据,这样丢失的数据会最少。
这期就讲到这里,下期继续。