- 作者:老汪软件技巧
- 发表时间:2024-08-25 15:11
- 浏览量:
本文为稀土掘金技术社区首发签约文章,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的旅程,探索其无尽的可能性!
AOF持久化
Redis不仅集成了高效的RDB(Redis Database)持久化机制,还独具匠心地引入了AOF(Append Only File)持久化技术,为数据持久性提供了更为灵活与详尽的解决方案。与RDB侧重于定期生成数据库快照的方式不同,AOF机制另辟蹊径,通过记录Redis服务器执行的所有写操作命令(如SET、INCR等),以日志的形式逐条追加到文件中,从而实现对数据库状态的全面追踪与记录,如下图展示的那样。
这种设计不仅使得数据恢复过程更加细致入微,能够精确到每一次写操作的执行,还赋予了用户根据实际需求调整日志记录频率与策略的能力,比如通过配置命令同步的时机(每秒、每命令或手动触发),来平衡数据安全性与磁盘I/O开销。
案例说明
举例来说,倘若我们在一个空白的数据库上执行以下写入操作指令,随后,该数据库中将会精准地填充上三个精心构建的键值对,从而赋予其初始的数据结构与内容。
redis>SET msg "hello world"
OK
redis>SADD seqNo 1 2 3
(integer)3
redis>RPUSH numbers 128 256 512
(integer)3
RDB持久化机制通过捕捉并保存数据库中特定键(如msg、seqNo、numbers)的键值对快照至RDB文件中,以此来实现数据库状态的持久化。相比之下,AOF持久化则采取了截然不同的策略,它专注于记录Redis服务器实际执行的写操作命令(如SET、SADD、RPUSH等),并将这些命令序列以追加的方式写入AOF文件中,从而实现对数据库状态变化的精确追踪与保存。
AOF文件格式
被写入AOF文件的所有命令均严格遵循Redis的命令请求协议格式进行保存,由于Redis的命令请求协议采用了易于阅读的纯文本格式,这一特性使得我们无需任何特殊工具即可直接打开AOF文件,进而直观地查看并理解文件中所记录的Redis操作命令及其具体内容,如下所示:
*2\r\n$6\r\nSELECT\r\n$1\r\no\r\n
*3\r\n$3\r\nSET\r\n$3\r\nmsg\r\n$11\r\hello world\r\n
*5\r\n$4\r\nSADD\r\n$6\r\nseqNo\r\n$5\r\napple\r\n$6\r\nbanana\r\ns6\r\ncherry\r\n
*5\r\n$5\r\nRPUSH\r\n$7\r\nnumbers\r\n$3\r\n128\r\n$3\r\n256\r\ns3\r\n512\r\n
在这份AOF(Append Only File)文件中,除了那些由服务器自动插入的、专门用于指定数据库的SELECT命令之外,其余所有内容均源自我们之前通过客户端精心发送的指令。
AOF启动的日志
当服务器启动时,它具备一项强大的功能,即通过加载并执行AOF文件中精心保存的指令序列,来精准地还原至服务器上次关闭前的数据库状态。这一过程不仅确保了数据的持续性和完整性,还极大地简化了数据恢复的操作流程。
[18321] 05 Aug 12:58:50.448 Server started,Redisversion 5.0.12
[18321] 05 Aug 12:58:50.449DB loaded from append only file:0.000 seconds
[18321] 05 Aug 12:58:50.449 The server is now ready to accept connections on port
6379
以上展示的日志,详细记录了服务器载入AOF文件并逐步还原数据库状态的全过程,每一步都清晰地反映了数据恢复工作的进展与成功。
AOF持久化的实买现
AOF持久化机制的实现精妙地划分为三个核心步骤:命令追加(append)、文件写入以及文件同步(sync)。首先,通过命令追加,服务器将接收到的每一个写操作指令以文本形式即时添加到AOF文件的末尾,确保了操作的即时记录。紧接着,在文件写入阶段,这些追加的命令被安全地存储至磁盘之中,构建起数据的持久化存储基础。
命令追加
当AOF(Append-Only File)持久化功能被激活启用时,服务器在完成一个写命令的执行流程后,会自动遵循协议的既定格式,将被成功执行的写命令追加至其内部维护的aof_buf缓冲区末端。这一操作确保了所有执行的写操作都能被准确地记录并保留,为后续的数据恢复与一致性校验提供了坚实保障。
struct redisServer{
//AOF 缓冲区
sds aof_buf;
}
举个具体的例子来说,当客户端向服务器发送如下命令时:
redis>SET KEY VALUE
OK
"SET KEY VALUE",这一指令旨在要求服务器在数据库中设置(或更新)一个键值对,其中键为KEY ,值为VALUE。
服务器接收到该命令后,会首先解析并执行它,即在内部数据结构中创建或更新相应的键值对。紧接着,如果AOF持久化功能处于启用状态,服务器还会按照AOF协议的规定,将这条SET命令以文本形式(如"SET KEY VALUE\r\n")追加到aof_buf缓冲区中,以便之后能够将这些命令写入AOF文件,实现数据的持久化存储。
* 3\r\ns3\r\nSET\r\ns3\r\nKEY\r\ns5\r\nVALUE\r\n
再举一例,当客户端向服务器发送如下指令时:
redis>RPUSH NUMBERS ONE TWO THREE
(integer)3
随后,服务器在执行了RPUSH命令之后,会将以下协议内容优雅地追加至AOF(Append Only File)缓冲区的末尾:
*5\r\n$5\r\nRPUSH\r\n$7\r\nNUMBERS\r\n$3\r\nONE\r\n$3\r\nTWo\r\n$5\r\nTHREE\r\n
AOF文件的写入与同步
Redis服务器进程核心是一个事件循环,分为文件事件和时间事件两类。文件事件处理客户端命令请求与回复,而时间事件则定期执行如serverCron等定时任务。
服务器在每个事件循环结束时,检查aof_buf缓冲区,若包含待写入命令,则调用flushAppendOnlyFile函数决定是否将其内容追加并保存到AOF文件中。伪代码如下:
def eventLoop():
while True:
# 处理文件事件,包括接收命令请求和发送命令回复
processFileEvents()
# 可能因处理命令请求而追加内容到AOF缓冲区
# 处理时间事件
processTimeEvents()
# 考虑并可能将AOF缓冲区内容写入并保存到AOF文件
flushAppendonlyFile()
这段代码清晰地展示了Redis事件循环的核心部分,包括处理文件事件(涉及客户端交互)、处理时间事件(如定期任务),以及在每次循环结束时考虑将AOF缓冲区内容写入AOF文件的过程。
flushAppendOnlyFile
flushAppendOnlyFile函数的行为受服务器配置的appendfsync选项值控制,不同值对应不同行为,如表11-1所示。此函数根据appendfsync的设置,决定何时将AOF缓冲区内容写入并同步到AOF文件中。
appendfsync值产生不同的持久化行为
若用户未显式设置appendfsync选项,其默认值为everysec。欲了解更多关于appendfsync选项的信息,请参阅Redis项目附带的示例配置文件redis.conf。
文件的写入和同步
为提高文件写入效率,现代操作系统在用户调用write函数写入数据时,会先将数据暂存于内存缓冲区。待缓冲区满或超时后,再批量写入磁盘。此方法虽高效,但存在安全风险:系统崩溃时,缓冲区数据可能丢失。为此,系统提供了fsync和fdatasync函数,可立即将缓冲区数据同步至硬盘,保障数据安全。
案例分析
举个例子,服务器在处理文件事件时,连续执行了三个写入命令。
SADD databases "Redis""MongoDB""MariaDB"
SET date "2013-9-5"
INCR click_counter 10086
那么,AOF buf(缓冲区)将包含这三个命令的协议化内容。
*5\r\n$4\r\nSADD\r\n$9\r\ndatabases\r\n$5\r\nRedis\r\n$7\r\nMongoDB\r\n$7\r\
nMariaDB\r\n
3\r\n$3\r\n$ET\r\n$4\r\ndate\r\n$8\r\n2013-9-5\r\n
*3\r\n$4\r\nINCR\r\n$13\r\nclick counter\r\n$5\r\n10086\r\n
若flushAppendonlyFile被调用,且appendfsync设为everysec且距上次同步已超一秒,服务器将先将aof_buf内容写入AOF文件,并同步文件。
AOF文件的载入与数据还原
AOF文件包含重建数据库所需的所有写命令,通过读取并执行这些命令,服务器能还原至关闭前的数据库状态。Redis读取AOF文件还原数据库状态的步骤简述如下:读取AOF,执行命令,恢复状态。
载入AOF时,Redis创建无网络连接的伪客户端执行AOF中的命令,效果等同于真实客户端执行,确保命令在客户端上下文中执行。
从AOF文件中解析并读取一条写命令。
使用伪客户端执行被读出的写命令。
循环执行步骤2和3,直至AOF文件中所有写命令处理完成。
完成上述步骤后,AOF文件所记录的数据库状态将被完整还原。
AOF重写
AOF持久化机制通过不断累积写命令来确保数据的安全性与一致性,然而,随着时间的推移,这些累积的命令会导致AOF文件体积逐渐增大。这一趋势不仅可能对Redis服务器的性能产生负面影响,还可能波及到宿主机的整体性能,如增加磁盘I/O负担、消耗更多的存储空间等。
redis>RPUSH list "A" "B“”
(integer) 2
redis>RPUSH list "C"
(integer)3
redis>RPUSH list "D""E
(integer)5
redis>LPOP list
“A”
“B”
redis>LPOP list
redis>RPUSH list "F""G"
(integer)5
在仅仅为了记录单一LIST键状态变更的情况下,AOF(Append Only File)文件便需承载多达六条命令的记录,这凸显了其在细致追踪数据变动方面的精确性。为了有效应对AOF文件体积不断增大的挑战,Redis巧妙地引入了AOF文件重写(rewrite)机制。这一机制的核心在于,Redis服务器能够智能地生成一个新的AOF文件,用以替换当前庞大的旧文件。
新旧AOF文件虽在物理上独立,却共同承载着相同的数据库状态,其关键差异在于:新文件经过深度优化,剔除了所有不必要的冗余命令,仅保留了恢复数据库状态所必需的最小命令集。
AOF文件重写的实现
Redis的“AOF文件重写”实际上不涉及旧AOF文件的直接读写操作,而是通过读取服务器当前的数据库状态来创建全新的AOF文件,以此替换旧文件。为应对AOF文件体积膨胀,Redis提供重写功能,创建更小的新AOF文件替换旧文件,两者保存相同数据库状态,但新文件去除冗余命令,体积显著减小。
redis>RPUSH list "A" "B“”
(integer) 2
redis>RPUSH list "C"
(integer)3
redis>RPUSH list "D""E
(integer)5
redis>LPOP list
“A”
“B”
redis>LPOP list
redis>RPUSH list "F""G"
(integer)5
优化服务器记录list键状态,高效方法是直接从数据库读list值,用一条RPUSH list "C" "D" "E" "E" "G"命令替代原六条,减少至一条命令。
除列表和集合键外,所有类型键均可通过读取当前值,以单条命令记录,替代多条旧命令,实现AOF重写功能,减少文件命令数量。
在目前版本中,REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值为64,这也就是说,如果一个集合键包含了超过64个元素,那么重写程序会用多条SADD命令来记录这个集合,并且每条命令设置的元素数量也为64个:
SADD ...
SADD ...
SADD ...
另一方面如果一个列表健包含了超过64个项,那么重写程序会用多条RPUSH命令来保存这个列表,并且每条命令设置的项数量也为64个:
RPUSH <1ist-key>...
RPUSH ...
RPUSH ..
重写程序使用类似的方法处理包含多个元素的有序集合键,以及包含多个健值对的哈希表健。
AOF后台重写
AOF重写程序aof_rewrite能有效生成新AOF文件,但因其涉及大量写入操作,调用时会长时间阻塞线程。鉴于Redis采用单线程处理命令,直接调用aof_rewrite会导致服务器无法响应客户端请求,直至重写完成。
AOF文件重写时的服务器进程和子进程
子进程负责AOF重写时,父进程(服务器)能继续处理命令。子进程拥有父进程数据副本,无需锁即可保证数据安全,因此选用子进程而非线程。子进程重写AOF时,服务器处理新命令可能修改数据库状态,导致与重写后AOF文件状态不一致,需解决此问题。
为解决数据不一致,Redis设立AOF重写缓冲区。子进程创建后,服务器执行写命令时,同步将命令发送至AOF缓冲区及AOF重写缓冲区,确保数据一致性。
在子进程AOF重写期间,服务器需执行三项工作,服务器同时将命令发送给AOF文件和AOF重写缓冲区:
执行客户端命令。将写命令追加至AOF缓冲区。并追加至AOF重写缓冲区。
AOF缓冲区内容定期写入并同步至AOF文件,保持对AOF文件的正常处理。自子进程创建起,所有写命令均被记录至AOF重写缓冲区。
AOF子线程完结处理步骤
子进程完成AOF重写后,向父进程发信号。父进程接收信号后,调用处理函数执行后续工作:
将AOF重写缓冲区内容写入新AOF文件,确保其与服务器当前数据库状态同步。原子性地对新AOF文件重命名,无缝替换旧文件,完成新旧AOF文件的平滑过渡。
此信号处理函数执行完成后,父进程恢复常规命令处理流程,无缝衔接。在整个AOF后台重写流程中,仅信号处理函数执行期间短暂阻塞父进程,其余时间均保持高效运作,从而最大限度地减少对服务器性能的影响。
最后总结
通过文件同步步骤,系统确保AOF文件中的数据与磁盘上的物理数据保持完全一致,即使面对系统故障也能有效保障数据的完整性和可恢复性。这一系列流程紧密衔接,共同构筑了AOF持久化功能的坚固基石。