Redis核心技术(问题解答篇)
01-基本架构:一个键值数据库(Redis)包含什么?
Redis能够在实际业务场景中得到广泛的应用,就是得益于支持多样化类型的value。
一个键值数据库包括了访问框架、索引模块、操作模块和存储模块四部分
Redis有四个重点关注方面:
1、Redis主要通过网络框架进行访问
2、Redis数据模型中的value类型很丰富
3、Redis的持久化模式能支持两种方式:日志(AOF)和快照(RDB)
4、Redis支持高可靠集群和高可靠扩展
高性能IO模型:为什么单线程Redis能那么快?
我要和你厘清一个事实,我们通常说,Redis是单线程,主要是指Redis的网络IO和键值对读写是由一个线程来完成的,这也是Redis对外提供键值存储服务的主要流程。但Redis的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。
AOF日志:宕机了,Redis如何避免数据丢失?
AOF机制给我们提供了三个选择,也就是AOF配置项appendfsync的三个可选值。
- Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
- Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
- No,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
07-哨兵机制:主库挂了,如何不间断服务?
哨兵主要负责的就是三个任务:监控、选主(选择主库)和通知。
(1)监控
监控是指哨兵进程在运行时,周期性地给所有的主从库发送PING命令,检测它们是否仍然在线运行。如果从库没有在规定时间内响应哨兵的PING命令,哨兵就会把它标记为“下线状态”;同样,如果主库也没有在规定时间内响应哨兵的PING命令,哨兵就会判定主库下线,然后开始自动切换主库的流程。
(2)选主
主库挂了以后,哨兵就需要从很多个从库里,按照一定的规则选择一个从库实例,把它作为新的主库。
(3)通知
在执行通知任务时,哨兵会把新主库的连接信息发给其他从库,让它们执行replicaof命令,和新主库建立连接,并进行数据复制。同时,哨兵会把新主库的连接信息通知给客户端,让它们把请求操作发到新主库上。
“客观下线”的标准就是,当有N个哨兵实例时,最好要有N/2 + 1个实例判断主库为“主观下线”,才能最终判定主库为“客观下线”。
如何选定新主库?
哨兵选择新主库的过程称为“筛选+打分”。简单来说,我们在多个从库中,先按照一定的筛选条件,把不符合条件的从库去掉。然后,我们再按照一定的规则,给剩下的从库逐个打分,将得分最高的从库选为新主库,如下图所示:
这三个规则分别是从库优先级、从库复制进度以及从库ID号。
- 第一轮:优先级最高的从库得分高。
- 第二轮:和旧主库同步程度最接近的从库得分高。
- 第三轮:ID号小的从库得分高。
用户可以通过slave-priority配置项,给不同的从库设置不同优先级。
在优先级和复制进度都相同的情况下,ID号最小的从库得分最高,会被选为新主库。
08-哨兵集群:哨兵挂了,主从库还能切换吗?
- 为了实现主从切换,我们引入了哨兵;
- 为了避免单个哨兵故障后无法进行主从切换,以及为了减少误判率,又引入了哨兵集群;
- 哨兵集群又需要有一些机制来支撑它的正常运行。
支持哨兵集群的这些关键机制,包括:
- 基于pub/sub机制的哨兵集群组成过程;
- 基于INFO命令的从库列表,这可以帮助哨兵和从库建立连接;
- 基于哨兵自身的pub/sub功能,这实现了客户端和哨兵之间的事件通知。
09-切片集群:数据增多了,是该加内存还是加实例?
切片集群是一种保存大量数据的通用机制
简单来说,bgsave子进程是由主线程fork生成的,可以共享主线程的所有内存数据。bgsave子进程运行后,开始读取主线程的内存数据,并把它们写入RDB文件。
如果频繁地执行全量快照,也会带来两方面的开销。
- 一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
- 另一方面,bgsave子进程需要通过fork操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁fork出bgsave子进程,这就会频繁阻塞主线程了。那么,有什么其他好方法吗?
此时,我们可以做增量快照,所谓增量快照,就是指,做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。
Redis 4.0中提出了一个混合使用AOF日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用AOF日志记录这期间的所有命令操作。
这样一来,快照不用很频繁地执行,这就避免了频繁fork对主线程的影响。而且,AOF日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。
如下图所示,T1和T2时刻的修改,用AOF日志记录,等到第二次做全量快照时,就可以清空AOF日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。
[图片上传失败...(image-48600f-1627629071132)]
这个方法既能享受到RDB文件快速恢复的好处,又能享受到AOF只记录操作命令的简单优势,颇有点“鱼和熊掌可以兼得”的感觉,建议你在实践中用起来。
12-有一亿个keys要统计,应该用哪种集合?
- 如果你只需要存储简单的键值对,或者是对数字进行递增递减操作,就可以使用String存储;
- 如果需要一个简单的分布式队列服务,List就可以满足你的需求;
- 如果除了需要存储键值数据,还想单独对某个字段进行操作,使用Hash就非常方便;
- 如果想得到一个不重复的集合,就可以使用Set,而且它还可以做并集、差集和交集运算;
- 如果想实现一个带权重的评论、排行榜列表,那么,Sorted Set就能满足你。