【专栏简介】
随着数据需求的迅猛增长,持久化和数据查询技术的重要性日益凸显。关系型数据库已不再是唯一选择,数据的处理方式正变得日益多样化。在众多新兴的解决方案与工具中,Redis凭借其独特的优势脱颖而出。
【技术大纲】
为何Redis备受瞩目?原因在于其学习曲线平缓,短时间内便能对Redis有初步了解。同时,Redis在处理特定问题时展现出卓越的通用性,专注于其擅长的领域。深入了解Redis后,您将能够明确哪些任务适合由Redis承担,哪些则不适宜。这一经验对开发人员来说是一笔宝贵的财富。
在这个专栏中,我们将专注于Redis的6.2版本进行深入分析和介绍。Redis 6.2不仅是我个人特别偏爱的一个版本,而且在实际应用中也被广泛认为是稳定性和性能表现都相当出色的版本。
【专栏目标】
本专栏深入浅出地传授Redis的基础知识,旨在助力读者掌握其核心概念与技能。深入剖析了Redis的大多数功能以及全部多机功能的实现原理,详细展示了这些功能的核心数据结构和关键算法思想。读者将能够快速且有效地理解Redis的内部构造和运作机制,这些知识将助力读者更好地运用Redis,提升其使用效率。
将聚焦于Redis的五大数据结构,深入剖析各种数据建模方法,并分享关键的管理细节与调试技巧。
【目标人群】
Redis技术进阶之路专栏:目标人群与受众对象,对于希望深入了解Redis实现原理底层细节的人群。
1. Redis爱好者与社区成员
Redis技术有浓厚兴趣,经常参与社区讨论,希望深入研究Redis内部机制、性能优化和扩展性的读者。
2. 后端开发和系统架构师
在日常工作中经常使用Redis作为数据存储和缓存工具,他们在项目中需要利用Redis进行数据存储、缓存、消息队列等操作时,此专栏将为他们提供有力的技术支撑。
3. 计算机专业的本科生及研究生
对于学习计算机科学、软件工程、数据分析等相关专业的在校学生,以及对Redis技术感兴趣的教育工作者,此专栏可以作为他们的学习资料和教学参考。
无论是初学者还是资深专家,无论是从业者还是学生,只要对Redis技术感兴趣并希望深入了解其原理和实践,都是此专栏的目标人群和受众对象。
让我们携手踏上学习Redis的旅程,探索其无尽的可能性!
Redis服务器的事件驱动程序
Redis服务器是一个事件驱动程序,它主要处理两类事件。
-
文件事件(file event):Redis服务器对套接字操作的抽象。当服务器通过套接字与客户端或其他 Redis 服务器连接时,通信会产生文件事件。服务器通过监听并处理这些事件来完成网络通信操作。
-
时间事件(time event) :Redis服务器中的一些操作(如 serverCron函数)需在特定时间点执行,时间事件就是对这类定时操作的抽象。
本文会介绍文件事件和时间事件,包括它们在 Redis 服务器中的应用、实现方法以及处理这些事件的 API 等。最后还会介绍服务器的事件调度方式,即 Redis 服务器如何安排并执行文件事件和时间事件。
文件事件
Redis数据库依照Reactor模式创建了属于自己的用于处理网络事件的机制,该机制被命名为文件事件处理器(file event handler)。
I/O多路复用机制
I/O 多路复用是一种技术,它可以让一个程序同时监视多个输入/输出源,比如多个套接字。当其中一个或多个源有事件发生时,程序能够及时响应。

文件事件处理器架构
它通过使用 I/O 多路复用程序,能够同时对多个套接字进行监听。它会依据套接字当前正在执行的任务,为套接字分配不同的事件处理器。 文件事件处理器利用这种技术,能够高效地管理多个套接字,根据每个套接字的具体任务情况,为其关联相应的事件处理器,以便在特定事件发生时进行恰当的处理。
例如,一个套接字可能在进行数据接收任务,此时就为其关联一个适合处理数据接收事件的处理器,另一个套接字可能在进行数据发送任务,那就为其关联一个用于处理数据发送事件的处理器。
文件处理器执行流程
首先,当被监听的套接字准备好进行连接应答、读取、写入、关闭等操作时,就会产生与这些操作相对应的文件事件。接着,文件事件处理器会调用套接字之前关联好的事件处理器来处理这些产生的事件。 例如,当有新的连接(数据可读或需要写入数据、关闭套接字)请求到来时,被监听的套接字准备好执行连接(数据可读或需要写入数据、关闭套接字)应答操作,此时就会产生连接(数据可读或需要写入数据、关闭套接字)应答对应的文件事件,文件事件处理器会调用之前关联好的连接应答事件处理器来处理这个新的连接请求。
文件处理器组成部分
文件事件处理器的四个组成部分,它们分别是套接字 、I/O多路复用程序 、文件事件分派器 (dispatcher)以及事件处理器 。 文件事件是对套接字操作的抽象,每当一个套接字准备好执行连接应答**(accept)**、写人 、读取 、关闭 等操作时,就会产生一个文件事件,因为一个服务器通常会连接多个套接字,所以多个文件事件有可能会并发地出现。
I/O多路复用程序负责监听多个套接字,并向文件事件分派器传送那些产生了事件的套接字。
执行队列
I/O 多路复用程序处理多个文件事件时的方式:
- 全局队列:多个文件事件可能并发出现,但I/O多路复用程序会将产生事件的套接字都放入一个队列中
- 有序执行:程序以有序、同步且每次一个套接字的方式向文件事件分派器传送套接字
- 串行处理:当上一个套接字产生的事件被处理完毕,I/O 多路复用程序才会传送下一个套接字
事件分类执行
文件事件分派器能够接收通过 I/O 多路复用程序传来的套接字。当接收到套接字后,它会依据套接字产生的事件的具体类型,调用与之相对应的事件处理器。 在服务器中,会针对执行不同任务的套接字关联不一样的事件处理器。而这些事件处理器是一个个的函数,它们明确规定了在某个特定事件发生的时候,服务器应当执行的具体动作。
I/O多路复用程序的实现
Redis的 I/O 多路复用程序通过包装常见的select、epoll、evport和kqueue等 I/O 多路复用函数库来实现所有功能,每个函数库在Redis源码中对应一个单独文件。由于Redis为每个函数库实现了相同的 API,所以底层实现可以互换。 Redis 在 I/O 多路复用程序的实现源码中用#include宏定义了相应规则,程序在编译时会自动选择系统中性能最高的I/O多路复用函数库作为 Redis 的 I/O 多路复用程序的底层实现。
编译时自动选择系统中性能最高的I/O多路复用函数库来作为Rdis的I/O多路复用程序的底层实现,基本程序如下所示:
c
# ifdef HAVE_EVPORT
# include "ae_evport.c"
# else
# ifdef HAVE_EPOLL
# include "ae epoll.c"
# else
# ifdef HAVE_KQUEUE
# include "ae kqueue.c"
# else
# include "ae select.c"
# endif
# endif
# endif
事件的类型
I/O 多路复用程序能够同时监听多个套接字的两种事件,即ae.h/AE_READABLE
事件和ae.h/AE_WRITABLE
事件。这两种事件与套接字操作存在特定的对应关系,如下所示:
- AE_READABLE:通常对应着套接字有可读数据的情况,比如接收到新的数据或者对端有数据发送过来等待读取。
- AE_WRITABLE:通常对应着套接字可写的情况,即可以向套接字写入数据而不会被阻塞。=
AE_READABLE
当套接字变得可读时,这包括两种情况,一是客户端对套接字执行"write 操作"后套接字变得可读,二是客户端执行"close 操作"时套接字也会变得可读。另外,当有新的可应答套接字出现时,即客户端对服务器的监听套接字执行"connect 操作"时,也会产生"AE_READABLE 事件"。
AE_WRITABLE
当网络中的套接字处于一种状态,使得可以向其写入数据时(也就是在客户端对这个套接字执行读取操作的时候),这个套接字就会触发一个名为 AE_WRITABLE 的事件。
这意味着套接字已经准备好接收要写入的数据,可以进行数据的发送操作。AE_WRITABLE 通常是在特定的网络编程框架或环境中定义的一种事件类型,用于通知程序可以对套接字进行写操作了。
AE_READABLE优先于AE_WRITABLE
I/O多路复用程序允许服务器同时监听套接字的AE_READABLE 事件和AE_WRITABLE 事件,如果一个套接字同时产生了这两种事件,那么文件事件分派器会优先处理AE_READABLE 事件,等到AE_READABLE事件处理完之后,才处理AE_WRITABLE事件。
如果一个套接字又可读又可写的话,那么服务器将先读套接字,后写套接字。
实际事件处理器
Redis 为文件事件编写了多个处理器,用于实现不同的网络通信需求。具体来说:
- 连接应答处理器:监听套接字关联连接应答处理器,以对连接服务器的各个客户端进行应答。
- 客户端套接字关联命令请求处理器,用于接收客户端传来的命令请求。
- 客户端套接字关联命令回复处理器,以便向客户端返回命令的执行结果。
- 当主服务器和从服务器进行复制操作时,主从服务器都要关联为复制功能编写的复制处理器。
- 指出在这些事件处理器中,服务器最常用的是与客户端进行通信的连接应答处理器、命令请求处理器和命令回复处理器。
连接应答处理器
在 Redis 中,有一个名为 networking.c 的文件,其中的 acceptTcpHandler 函数承担着连接应答处理器的角色。
初始化时操作过程
程序会将连接应答处理器和服务器监听套接字的AE_READABLE事件关联起来。
当有客户端使用sys/socket.h/connect
函数连接服务器监听套接字时,该套接字会产生AE_READABLE事件,这个事件会引发连接应答处理器执行,并且执行相应的套接字应答操作。
请求连接时候操作
对连接服务器监听套接字的客户端连接应答 ",这个函数的作用是当有客户端连接到服务器的监听套接字时,该函数会对客户端的连接请求进行回应。
底层具体实现为
sys/socket.h/accept
函数的包装"指出 acceptTcpHandler 函数实际上是对sys/socket.h
中的accept函数进行了封装。
命令请求处理器
networking.c/readQueryFromClient
函数是Redis的命令请求处理器,负责从套接字中读取客户端发送的命令请求内容,其具体实现是对unistd.h/read
函数的包装。
- 当一个客户端通过连接应答处理器成功连接到服务器之后,服务器会将客户端套接字的AE_READABLE事件和命令请 求处理器关联起来
- 当客户端向服务器发送命令请求的时候,套接字就会产生AE_READABLE事件,引发命令请求处理器执行,并执行相应的套接字读入操作。
在客户端连接服务器的整个过程中,服务器都会一直为客户端套接字的AE_READABLE事件关联命令请求处理器。
命令回复处理器
networking.c/sendReplyToclient
函数是Redis的命令回复处理器。命令回复处理器将服务器执行命令后得到的命令回复通过套接字返回给客户端,它的主要功能是把 Redis 服务器执行完命令后产生的回复信息,借助套接字这种通信方式传递给客户端。
sendReplyToclient
函数实际上是对unistd.h
中的write函数进行了封装,以实现将命令回复发送给客户端的特定功能。
具体流程
- 当服务器有命令回复需要传送给客户端的时候,服务器会将客户端套接字的AE_WRITABLE事件和命令回复处理器关联起来。
- 当客户端准备好接收服务器传回的命令回复时,就会产生AE_WRITABLE事件,引发命令回复处理器执行,并执行相应的套接字写入操作。
- 当命令回复发送完毕之后,服务器就会解除命令回复处理器与客户端套接字的AE_WRITABLE事件之间的关联。
总体流程架构(7步)
- 当Redis服务器运行时,监听套接字的AE_READABLE事件处于监听状态,对应的处理器是连接应答处理器。
- 若有Redis客户端发起连接,监听套接字产生AE_READABLE事件,触发连接应答处理器。
- 处理器应答请求,创建客户端套接字和状态,并将客户端套接字的AE_READABLE事件与命令请求处理器关联,使客户端能发命令。
- 客户端发送命令请求时,客户端套接字产生AE_READABLE事件,触发命令请求处理器读取命令内容并传相关程序执行。
- 执行命令会有回复,服务器将客户端套接字的AE_WRITABLE事件与命令回复处理器关联。
- 客户端读取回复时,客户端套接字产生AE_WRITABLE事件,触发命令回复处理器。
- 回复写入套接字后,服务器解除关联。