本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
【专栏简介】
随着数据需求的迅猛增长,持久化和数据查询技术的重要性日益凸显。关系型数据库已不再是唯一选择,数据的处理方式正变得日益多样化。在众多新兴的解决方案与工具中,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作为高性能的键值存储系统,其核心职责在于高效地与众多客户端建立并维护网络连接,处理来自这些客户端的多样化命令请求。 在执行这些命令时,Redis不仅负责在其内部数据库中存储和更新由客户端操作产生的数据,还通过精细的资源管理机制确保服务器自身的稳定运行与性能优化。
命令执行流程
针对于分析服务器内部各组件(如命令解析器、执行引擎、数据存储模块等)如何紧密协作,共同完成命令的接收、解析、执行与响应反馈。
serverCron函数
serverCron函数是Redis服务器中的核心定时任务处理函数,其重要性不言而喻。深入剖析serverCron函数所执行的一系列操作,包括但不限于内存管理(如过期键清理)、持久化任务(如AOF和RDB文件的写入)、客户端连接管理(如超时检测与断开)、统计信息收集等。
服务启动流程
从启动服务器程序的那一刻起,直至服务器准备就绪,能够接受并处理来自客户端的命令请求,这中间经历了多个关键步骤。逐一揭示这些步骤,包括初始化配置参数、创建网络连接监听、启动后台线程(如serverCron)、加载持久化数据等。
命令请求的执行过程
探讨一个命令请求从发起至获取响应的全过程时,客户端与服务器之间需紧密配合,历经一系列精心设计的交互步骤。以客户端执行特定命令为例,这一过程不仅仅是简单的信息发送与接收,而是涉及了多个复杂而精细的操作环节。
bash
redis>SET KEY VALUE
OK
客户端发送SET KEY VALUE命令至接收到OK回复的过程中,客户端与服务器之间协同工作的细节变得尤为关键。这一周期性的交互涵盖了多个精心编排的步骤,确保了命令的准确传达、高效处理及成功响应。
-
客户端发起行动,将
SET KEY VALUE
的指令请求精准地传递给服务器,启动了数据更新流程的起点。 -
服务器接收到来自客户端的
SET KEY VALUE
指令后,立即启动处理机制。它解析指令内容,随后在内部数据库中执行相应的设置操作,确保数据的一致性与准确性。操作成功后,服务器生成了OK
的确认回复,标志着命令的成功执行。 -
服务器将这份
OK
的确认回复封装成网络可识别的数据包,并通过高效的网络通信协议将其安全地传回给客户端,实现了信息的即时反馈。 -
客户端在接收到服务器的
OK
回复后,迅速进行解析与验证,确认操作已成功完成。随后,它以一种直观且友好的方式,将这个成功的反馈呈现给用户,让用户能够即时了解到操作的结果。
发送命令请求
当输入一个命令请求时,客户端立即启动其内置的转换机制,将这一人类可读的命令请求转换为Redis协议所规定的格式。这一过程不仅确保了命令的标准化,还使其能够在网络中高效、安全地传输。 客户端利用已经与Redis服务器建立的稳定连接一个基于套接字的通信通道,将转换后的协议格式命令请求发送给服务器。通过一个实例来深化这一过程的理解。在Redis客户端中,轻轻敲下了这样一个命令:
bash
SET KEY VALUE
这个看似简单的指令,实则蕴含了数据存储与检索的复杂逻辑。客户端便迅速响应,将这一人类语言编写的命令,巧妙地转化为Redis服务器能够理解的协议格式。转换后的协议内容,以一种结构化的方式展现,如下所示:
bash
*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n
读取命令请求
当客户端与服务器之间建立的连接套接字,因客户端的写入操作而变得具备读取条件时,服务器会触发一个机制,该机制随即调用命令请求处理器以执行一系列精心设计的操作。
-
解析与存储协议命令:服务端读取套接字中按照既定协议格式编排的命令请求,随后将这些精确的数据安全地保存到专为客户端维护的输入缓冲区中。
-
命令参数的抽取与分类:系统对输入缓冲区内的命令请求进行深入分析,精确地提取出命令请求所蕴含的命令参数及其数量。
系统将这些宝贵的参数及其数量信息,分别精心地存储到客户端状态中的
argv
(参数值数组)和argc
(参数数量)属性里,以便后续命令执行时能够高效访问。
- 执行指定命令:完成了命令参数的准备工作后,系统无缝地调用命令执行器,依据客户端明确指定的命令,启动执行流程。
延续前一小节关于SET命令的探讨,程序在成功将命令请求存储至客户端状态的输入缓冲区后,该客户端状态所呈现出的具体形态。
我们针对于对输入缓冲区中的协议进行分析:*3\r\ns3\r\nSET\r\ns3\r\nKEY\r\ns5\r\nVALUE\r\n
得出的分析结果保存到客户端状态的argv属性和argc属性里面。
命令执行器 --- 查找命令实现
当命令执行器启动其处理流程时,首要任务是依据客户端请求中的argv[0]参数,该参数作为标识符,用于在预定义的命令表(command table)中精确检索目标命令。这一查找过程是基于键值对映射的逻辑,其中命令表被巧妙地设计为一个字典结构,其键(key)直接对应于Redis支持的各类命令名称,诸如"set"、"get"、"del"等常用操作;而每个键所对应的值(value),则是一个精心构造的redisCommand结构体实例。
redisCommand结构的主要属性
redisCommand结构体,作为Redis命令实现信息的载体,详尽地记录了每个命令的核心属性与功能特性。这些属性包括但不限于命令的处理函数指针、命令的参数数量、命令的读写类型、以及命令执行时的权限检查要求等,它们共同定义了一个Redis命令的完整行为轮廓。
属性名 | 类型 | 作用 |
---|---|---|
name | char | 命令的名字,比如"set" |
proc | redisCommandProc | 函数指针,指向命令的实现函数,比如setCommand。redisCommandProc定义为typedef void redisCommandProc(redisClient *c); |
arity | int | 命令参数的个数,用于检查命令请求的格式是否正确。如果这个值为负数-N,那么表示参数的数量大于等于N。注意命令的名字本身也是一个参数,比如SET msg "hello world"命令的参数是"SET"、"msg"、"hello world",而不仅仅是"msg"和"hello world" |
sflags | char* | 字符串形式的标识值,这个值记录了命令的属性,比如这个命令是写命令还是读命令,这个命令是否允许在载入数据时使用,这个命令是否允许在Lua脚本中使用等等 |
flags | int | 对sflags标识进行分析得出的二进制标识,由程序自动生成。服务器对命令标识进行检查时使用的都是flags属性而不是sflags属性,因为对二进���标识的检查可以方便地通过位运算(如&、|、~)来完成 |
calls | long long | 服务器总共执行了多少次这个命令 |
milliseconds | long long | 服务器执行这个命令所耗费的总时长(以毫秒为单位) |
sflags属性的标识
标识 | 意义 | 带有这个标识的命令 |
---|---|---|
w | 这是一个写入命令,可能会修改数据库 | SET、RPUSH、DEL等 |
r | 这是一个只读命令,不会修改数据库 | GET、STRLEN、EXISTS等 |
m | 这个命令可能会占用大量内存,执行前要先检查服务器的内存使用情况,如果内存紧缺就禁止执行这个命令 | SET、APPEND、RPUSH、LPUSH、SADD、SINTERSTORE等 |
a | 这是一个管理命令 | SAVE、BGSAVE、SHUTDOWN等 |
p | 这是一个发布与订阅功能方面的命令 | PUBLISH、SUBSCRIBE、PUBSUB等 |
s | 这个命令不可以在Lua脚本中使用 | BRPOP、BLPOP、BRPOPLPUSH、SPOP等 |
R | 这是一个随机命令,对于相同的数据集和相同的参数,命令返回的结果可能不同 | SPOP、SRANDMEMBER、SSCAN、RANDOMKEY等 |
S | 当在Lua脚本中使用这个命令时,对这个命令的输出结果进行一次排序,使得命令的结果有序 | SINTER、SUNION、SDIFF、SMEMBERS、KEYS等 |
l | 这个命令可以在服务器载入数据的过程中使用 | INFO、SHUTDOWN、PUBLISH等 |
A | 这是一个允许从服务器在带有过期数据时使用的命令 | SLAVEOF、PING、INFO等(注意:这里我假设了PNG 可能是PING 的误写,WFO 可能不是标准Redis命令,因此我替换了它) |
M | 这个命令在监视器(monitor)模式下不会自动被传播(propagate) | EXEC(注意:通常EXEC命令与事务相关,不直接与监视器模式传播相关,但这里按您的要求列出) |
redisCommand
SET命令,其标识性名称为"set",通过setCommand
函数实现具体功能。该命令的参数个数被设定为-3,这一独特设计巧妙地表示SET命令能够接收至少三个参数,以灵活应对多样化的数据设置需求。同时,SET命令被赋予了"wm"标识,这双重标记不仅揭示了其作为写入命令的本质,即能够修改数据库状态,还强调了执行前需对服务器内存状况进行评估的重要性,以防因内存占用过多而导致服务不稳定。 GET命令则以"get"为名,通过getCommand
函数简洁明了地实现了数据检索功能。其参数个数严格设定为2,明确指出了GET命令操作所需的精确参数数量,体现了Redis命令设计的严谨性。而"r"标识的赋予,则直观表明了GET命令的只读特性,即该命令不会对数据库状态产生任何修改。
最后,服务器内部各组件之间的紧密协作是一个高度复杂而精细的过程,它依赖于各组件之间的高效沟通与协同工作。通过不断优化这一流程,我们可以进一步提升服务器的性能与稳定性,为用户提供更加优质、高效的服务体验。