Redis设计与实现第11章 -- AOF持久化 总结(实现 重写)

RDB持久化通过保存数据库中的键值对来记录数据库状态的不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态

11.1 AOF持久化的实现

分为命令追加、文件写入和文件同步三个步骤

11.1.1 命令追加

当AOF持久化功能处于打开状态时,服务器在执行完一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾
struct redisServer {sds aof_buf;//AOF缓冲区 
};

11.1.2 AOF文件的写入和同步

Redis的服务器进程就是一个事件循环loop,这个循环中的文件事件负责接受客户端的命令请求,以及向客户端发送命令请求,而时间事件负责执行向serverCron函数这样的定时任务

服务器每次结束一个事件循环之前,都会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲区的内容写入和保存到AOF文件里

def eventLoop():while True :#处理文件事件,接收命令请求以及发送命令回复#处理命令请求时可能会有新内容被追加到aof_buf缓冲区中processFileEvents()#处理时间事件processTimeEvents()#考虑是否要将 aof_buf中的内容写入和保存到 AOF 文件里面flushAppendOnlyFile()

flushAppendOnlyFile函数的行为由服务器配置的appendfsync选项的值来决定,各个不同值产生的行为如下所示

  • always:将aof_buf缓冲区中的所有内容写入并同步到AOF文件
  • everysec:将aof_buf缓冲区的所有内容写入到AOF文件里,如果上次同步AOF文件的事件距离现在超过一秒钟,那么再次对AOF文件进行同步,同步操作由一个专门的线程负责执行
  • no:将aof_buf缓冲区中的所有内容**写入到AOF文件里但是不同步 **

默认值为everysec

AOF持久化的效率和安全性

  • appendfsync = always:效率最慢但是最安全
  • appendfsync = everysec:足够快,最多丢失一秒的数据
  • appendfsync = no:写入速度最快,但是最不安全11.2 AOF文件的载入与数据还原
    服务器只需要读取并重新执行一遍AOF文件里面保存的写命令就可以还原服务器关闭之前的数据库状态
    详细步骤
  1. 创建一个不带网络连接的伪客户端:因为Redis的命令只能在客户端上下文中执行
  2. 从AOF文件里分析并读取一条写命令
  3. 使用伪客户端执行被读取的写命令
    4.一直重复步骤2和3,直到所有的写命令被处理完毕

11.3 AOF重写

随着服务器运行时间的增加,AOF文件里的内容会越来越多,文件的体积也会越来越大,会对Redis服务器甚至宿主机造成影响,而且AOF文件的体积越大,使用AOF文件来进行数据还原所需的时间就越多
因此Redis提供了AOF重写功能,Redis服务器创建了一个新的AOF文件来替代现有的AOF文件,两个文件的AOF保存的数据库状态相同但是新的AOF文件不包含任何浪费空间的冗余命令,所以体积会小

11.3.1 AOF文件重写的实现

这个功能实际上是通过读取服务器当前的数据库状态实现的,比如向list里写入了6条数据,如果写到AOF文件里的话,就是6行数据,但是如果读取list的值,就可以用1行数据实现。
基本原理就是从数据库读取现在键的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令

def aof_rewrite(new_aof_file_name):#创建新 AOF 文件f=create_file(new_aof_file_name)#遍历数据库 for db in redisServer.db:#忽略空数据库if db.is_empty(): continue #写入SELECT命令 指定数据库号码 f.write_command("SELECT" + db.id)#遍历数据库中的所有键for key in db:# 忽略已过期的键if key.is_expired(): continue# 根据键的类型进行重写if key.type == string:rewrite_string(key)elif key.type == List:rewrite_list(key)elif key.type == Hash:rewrite_hash(key)elif key.type == Set:rewrite_set(key)elif key.type == SortedSet:rewrite_sorted_set(key)#如果键带有过期时间,那么过期时间也要被重写if key.have expire time():rewrite_expire_time(key)#写入完毕 关闭f.close()def rewrite_string(key):#使用GET命令获取字符串键的值value = GET(key)#使用SET命令重写字符串键f.write_command(SET,key,value)def rewrite list(key):#使用LRANGE 命令获取列表键包含的所有元素iteml,item2,……itemN=LRANGE(key,0-1)#使用RPUSH命令重写列表键f.write_command(RPUSH,key,iteml,item2,...itemN)
def rewrite_hash(key):#使用HGETALL命令获取哈希键包含的所有键值对fieldl,valuel,field2,value2,……fieldN,valueN = HGETALL(key)#使用HMSET命令重写哈希键f.write_command(HMSET, key,fieldl,valuel,field2,value2,...fieldN,valueN)
def rewrite_set(key):#使用SMEMBERS 命令获取集合键包含的所有元素eleml,elem2,...elemN = SMEMBERS(key)#使用SADD 命令重写集合键f.write_command(SADD,key,eleml,elem2,..elemN)
def rewrite_sorted_set(key):#使用ZRANGE命令获取有序集合键包含的所有元素member1,score1,member2,score2,……,memberN,scoreN = ZRANGE(key,0,-1,"WITHSCORES")#使用ZADD命令重写有序集合键f.write_command(ZADD,key,score1,member1,score2,member2,……,scoreN,memberN)def rewrite_expire_time(key):#获取毫秒精度的键过期时间戳timestamp=get_expire_time_in_unixstamp(key)#使用PEXPIREAT命令重写键的过期时间f.write_command(PEXPIREAT,key,timestamp)

在实际中,为了避免在执行命令时造成客户端输入缓冲区溢出,重写程序在列表、哈希表、集合、有序集合这四种可能带有多个元素的键时,会先检查键所包含的元素数量,如果元素数量超过了redis.h/REDIS_AOF_REWRITE_ITEMS_PRE_CMD常量的值,程序会用多条命令来记录,而不是单一命令。目前默认值为64

11.3.2 AOF后台重写

Redis如果用主线程处理重写的话,会造成长时间阻塞,所以使用子进程来执行。 子进程在进行AOF重写的时候,服务器进程(父进程)可以继续处理命令请求;子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下保证数据的安全性。 但是要解决的一个问题是,当子进程在进行AOF重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,从而使得服务器当前数据库状态和重写后的AOF文件所保存的数据库状态不一致。 为了解决这个问题,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当Redis服务器执行完一个写命令或,会同时把这个命令发送给AOF缓冲区和AOF重写缓冲区。 这样就可以保证:AOF缓冲区的内容会定期被写入和同步到AOF文件,对现有AOF文件的处理工作也会如常进行;从创建子进程开始,服务器执行的所有写命令都会被记录到AOF缓冲区里,当子进程完成AOF重写工作后,会向父进程发送一个信号,父进程收到信号后,调用信号处理函数并执行以下工作:
  1. 将AOF重写缓冲区的所有内容写入到新AOF文件里
  2. 对新的AOF文件进行改名,原子性地覆盖现有地AOF文件,完成替换

在整个过程里,只有信号处理函数执行时会对服务器进程造成阻塞,对服务器性能的影响降到了最低。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/19088.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

网络协议(4)拥塞控制

之前已经说过了tcp也是会考虑网络的情况的,也就是当网络出现问题的时候tcp不会再对报文进行重传。当所有的用户在网络不好的时候都不会对丢失的报文进行重传。这样就会防止网络瘫痪。 这样的机制也就是tcp会进行拥塞控制。 拥塞控制 所谓的慢启动看下面这张图就能…

#define定义宏(2)

大家好,今天给大家分享两个技巧。 首先我们应该先了解一下c语言中字符串具有自动连接的特点。注意只有将字符串作为宏参数的时候才可以把字符串放在字符串中。 下面我们来讲讲这两个技巧 1.使用#,把一个宏参数变成对应的字符串。 2.##的作用 可以把位…

蓝桥杯每日真题 - 第17天

题目:(最大数字) 题目描述(X届 C&C B组X题) 题目分析: 操作规则: 1号操作:将数字加1(如果该数字为9,变为0)。 2号操作:将数字减…

Leetcode打卡:最少翻转次数使二进制矩阵回文I

执行结果:通过 题目:3239 最少翻转次数使二进制矩阵回文I 给你一个 m x n 的二进制矩阵 grid 。 如果矩阵中一行或者一列从前往后与从后往前读是一样的,那么我们称这一行或者这一列是 回文 的。 你可以将 grid 中任意格子的值 翻转 &#…

@JsonSerialize修复前端精度问题

后端id定位为Long类型,前端查询出来的值莫名多了几个000 造成这个问题的原因是精度丢失, java中long数据能表示的范围比js中number大,在跟前端交互时,这样也就意味着部分数值在js中存不下(变成不准确的值)。 在字段上加 JsonSeri…

大模型(LLMs)RAG 版面分析——表格识别方法篇

大模型(LLMs)RAG 版面分析——表格识别方法篇 一、为什么需要识别表格? 表格的尺寸、类型和样式展现出多样化的特征,如背景填充的差异性、行列合并方法的多样性以及内容文本类型的不一致性等。同时,现有的文档资料不…

基于Matlab PCA人脸识别(二)

1.2 向量与基变换 1.2.1 内积与投影 两个大小相同向量的内积被定义如下:

RE正则表达式 小练习

题目: 答案:

整理:4篇专注于多模态大语言模型(MLLM)的瘦身变体论文

近年来,随着人工智能技术飞速发展,大语言模型(LLM)和多模态大语言模型(MLLM)成为了炙手可热的明星。它们不仅能处理文字,还能看图识字,简直是“全能选手”。这种能力得益于模型中加入…

车轮上的科技:Spring Boot汽车新闻集散地

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理汽车资讯网站的相关信息成为必然。开发合适…

go-zero(五) 模板定制

go-zero 模板定制 goctl 代码生成是基于 go 的模板去实现数据驱动的,实际开发中,使用goctl 生成的代码,并不符合我们的需求。 例如,我们刚刚的使用错误管理,我们需要在handler中返回的错误信息。 一、生成模板 首先…

ICML24最新开源时序基础模型MOMENT

论文标题:MOMENT: A Family of Open Time-series Foundation Models 论文链接:https://arxiv.org/pdf/2402.03885 前言 当前时间序列数据上预训练大型模型面临以下挑战:(1) 缺乏大型且统一的公共时间序列数据集,(2) 时间序列特…

Flink和Spark的区别是什么?各自的应用场景是什么?

一、Flink是什么? Flink:Flink 是一个分布式流处理框架,其架构基于流计算,将一切都看作是流。它采用了一种基于事件驱动的架构,数据以流的形式源源不断地进入系统,并且能够实时处理这些数据。例如&#xf…

2024.11.18晚Linux复习课笔记

第一章 cat -n显示行号 -b不显示空行号 pwd 打印当前的工作目录 cd ls 打印当前工作的所有文件 -a -A -l:显示当前文件的详细信息 -r:递归显示 passwd:修改密码 ip a 查看ip地址 poweroff shutdown -h 关机 reboot shutdown -r 第二章 man --help …

基于Spring Boot+Unipp的博物馆预约小程序(协同过滤算法、二维码识别)【原创】

🎈系统亮点:协同过滤算法、二维码识别; 一.系统开发工具与环境搭建 1.系统设计开发工具 后端使用Java编程语言的Spring boot框架 项目架构:B/S架构 运行环境:win10/win11、jdk17 前端: 技术:框…

Scaling Law的“终结“还是新起点?——开源实践者的深度思考

作者:宋大宝,与大宝同学因那篇《回顾总结展望「融合RL与LLM思想,探寻世界模型以迈向AGI」》结识于今年春天,虽我们当时某些思想观念有些出入,也碰撞出了很多火花与共鸣,并持续地相互启发的走到了现在。他是…

【qt】控件4

1.Qradiobutton(单选按钮) ui界面有三个按钮,应该文本框,根据不同的按钮来改变不同文本框的内容 根据不同的单选按钮改变不同的文本框。 Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);ui->radioB…

Day02_AJAX综合案例 (黑马笔记)

Day02_AJAX综合案例 目录 Day02_AJAX综合案例 学习目标 01.案例_图书管理-介绍 目标 讲解 小结 02.Bootstrap 弹框_属性控制 目标 讲解 小结 03.Bootstrap 弹框_JS控制 目标 讲解 小结 04.案例_图书管理_渲染列表 目标 讲解 小结 05.案例_图书管理_新增图书…

六、代码生成,《编译原理》(本科教学版),第2版

文章目录 零、前言0.1 编译器前端到后端 一、代码生成1.1 代码生成的任务1.2 给数据分配计算资源1.3 给代码选择合适的机器指令1.4 栈式计算机1.4.1 栈式计算机Stack的结构1.4.2 栈计算机的指令集1.4.3 变量的内存分配伪指令1.4.4 栈式计算机的代码生成1.4.4.1 递归下降代码生成…

Android集成FCM(Firebace Cloud Messaging )

集成FCM官方文档 Firebace主页面 将 Firebase 添加到您的 Android 应用 1、进入Firebace页面,创建自己的项目 2、点击自己创建好的项目,在右侧选择Cloud Messaging 3、点击Android去创建 google-services.json 4、将下载的 google-services.json 文件…