- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
Redis 的持久化功能是区别于 Memcached 显著特性,数据持久化可以保证系统在发生宕机和重启后数据不会丢失,对于 redis 这种存储在内存中的数据库显得尤为重要。 在 Redis 4.0 以前数据持久化的方式主要有两种
RDB 是将内存中的数据在某一时刻的状态记录下来以二进制的方式存储到磁盘中,通过生成一个经过压缩的二进制文件来实现数据库状态的复原。默认是 dump.rdb 文件,它的优点是以二进制存储,占用空间更小,数据存储更紧凑。
因为 RDB 文件是保存在磁盘中,哪怕 Redis 服务器宕机,Redis 服务器就可以通过该文件来还原数据库状态。
触发RDB持久化既可以通过手动执行,也可以根据服务器配置选项定期执行。主要分为手动触发和自动触发两种方式。
手动触发主要是通过save 和 bgsave 两个命令来生成RDB 文件
自动触发主要是通过 redis.conf 配置文件来完成:
# save <seconds> <changes> 周期性执行RDB持久化条件的设置格式
# 默认配置
save 900 1
save 300 10
save 60 10000
# 文件名称
dbfilename dump.rdb
# 文件保存路径
dir /home/work/app/redis/data/
# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes
# 是否压缩
rdbcompression yes
# 导入时是否检查,这里是使用CRC64算法来进行数据校验,会增加性能消耗
rdbchecksum yes
还有其他情况也可能会导致自动触发:
数据量较大时进行快照,用时相对会比较长。如果服务器在这个期间收到写请求,那么就不能保证快照的完整性。那么Redis 是如何做的?
Redis 使用的是操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。
如下图,针对 bgsave 命令触发 RDB 机制来说,如果主线程要修改一块数据(图中的键值对 C),这块数据就会被复制一份,生成该数据的副本(键值对C')。此时中线程在该副本上进行修改,bgsave 子线程继续把原来的数据(键值对 C)写入 RDB 文件中。
这样既保证了RDB 文件的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
数据没有全部写入磁盘前,该次快照操作都是失败的。将以这次快照操作前的完整 RDB 快照文件作为恢复内存的数据。也就是此次快照操作过程不能影响上一次的备份数据。实际上在操作时 Redis 服务会在磁盘上创建一个临时文件来进行数据操作,待操作成功后才会用这个临时文件替换上一次的备份数据。
频繁的执行全量快照,能够带来更快的恢复速度。但同时也会带来大量的开销,给磁盘带来压力,多个快照竞争有限的磁盘带宽,容易造成恶性循环。此外, bgsave 子进程需要通过 fork 操作从主线程创建出来。如果频繁的fork 出 bgsave 子进程就会阻塞主线程。因此 Redis 在有一个 bgsave 运行时,就不会再启动第二个 bgsave 子进程。
这里就可以采用增量快照的方法,就是在全量快照后,后续的快照只对修改后的数据进行快照,避免对数据重复的那部分进行快照带来的消耗,这样更加高效。这里就需要对修改的文件数据进行记录,如下图所示:
但是,我们在增量快照时,记录所修改的数据信息也是一部分的开销,在大量数据修改时的记录数据,其内存开销也不少。所以对于做快照的频率,不能太高也不能太低。那么有什么解决方法既能够让 RDB 快速恢复,又能以较小的开销实现少丢数据呢?
Redis 4.0 提出了 混合使用 AOF 日志和内存快照的方法可以很好的解决上面的场景。
AOF (Append Only File)日志和大多数的数据库写入日志的方式不同,采用的是写后日志。(比如 MySQL 是通过写前日志(Write Ahead Log, WAL)和两阶段提交来实现数据和逻辑的一致性)
写后日志的意思是 Redis 先将数据写入内存,然后再记录日志,如下图:
采用后写日志有这样的好处:
但是在写回磁盘时也会产生风险:
这两个风险是 AOF 写回磁盘时机不当造成的,对于这两个风险,AOF 有三种写回策略可以避免。也就是在文件写入同步时发生。
AOF 持久化功能的实现可以分为命令追加(append)、文件写入(write)和文件同步(sync)三个步骤。
当 AOF 持久化功能处于打开状态时,服务器在执行完一个写命令之后,就会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾中,这个 aof_buf 是以 sds 对象形式存储。
struct redisServer {
...
// AOF 缓冲区
sds aof_buf;
...
};
以Redis 服务端收到 set testkey testvalue
命令后的记录为例,查看 AOF 日志的运行原理。
在服务器执行 set 命令后,会将以下协议内容追加到 aof_buf 缓冲区的末尾:
*3\r\n$3\r\nSET\r\n$7\r\ntestkey\r\n$9\r\ntestvalue\r\n
*3
:表示当前命令有三个部分,每个部分都是由$+数字
开头,后面紧跟具体的命令、键或值\r\n
:是换行符,具体如上图所示$+数字
:数字表示这部分的命令、键或值一共有多少个字节,比如 $3 set
表示这部分有三个字节的命令,也就是 set
命令Redis 的服务器进程就是一个事件循环(loop),在处理文件事件时可能会执行写命令,所以在服务器每次结束一个事件循环之前,它会调用 flushAppendOnlyFile
函数,考虑是否需要将 aof_buf 缓冲区中的内容写入到AOF 文件中去。
def eventLoop():
while True:
//处理文件事件,接收命令请求以及发送命令回复
//处理命令请求时可能会有新内容被追加到 aof_buf缓冲区中
processFileEvents();
//处理时间事件
processTimeEvents();
//考虑是否要将 aof_buf中的内容写入和保存到 AOF文件里面
flushAppendOnlyFile();
而flushAppendOnlyFile
函数的行为有服务器配置的 appendfsync 选项的值来决定。其值主要有三种:
在保存了 AOF 文件后,服务器只需要重新执行 AOF 文件中的写命令后,就可以还原服务器关闭之前的数据库状态,其具体步骤如下:
因为 AOF 是通过保存被执行的写命令来完成 Redis 持久化的,所以随着 Redis 的运行时间越长,AOF 文件中存储的日志内容会越来越多。会给 Redis 服务器,带来很大的影响。因此 Redis 提供了 AOF 文件重写功能解决这一痛点。
重写机制具有“多变一”的功能,也就是对旧日志文件中的多条命令,在重写后的新日志变成了一条命令。如下图所示:
AOF 重写过程是由后台子进程 bgrewriteaof 来完成,重写过程是一个拷贝,两处日志:
一个拷贝:是指每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程。此时,fork 会把主线程的内存拷贝一份给 bgrewriteaof。所以这个子线程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。
两处日志:
第一处日志指的是正在使用的 AOF 日志,Redis 会把这个操作写到它的缓冲区。即使宕机了,这个 AOF 日志的操作仍然是齐全的可以用于恢复。
第二处日志指的是新 AOF 重写日志,这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作
所以说在每次 AOF 重写时,Redis 会先执行一份内存拷贝,用于重写;然后使用两个日志保证在重写过程中新写入的数据不会丢失。同时因为 bgrewriteaof 子进程的出现,这个过程并不会阻塞主线程。
有两个配置项可以控制 AOF 重写的触发:
auto-aof-rewrite-min-size
: 表示运行 AOF 重写时文件的最小大小,默认是 64MBauto-aof-rewrite-percentage
: 这个值是当前 aof 文件和上一次重写后 aof 文件差值,再除以上一次重写 aof 文件大小。在介绍 RDB 机制时,我们了解到是否有一种方法既能利用 RDB 的快速恢复,又能以较小的开销做到尽量少丢数据?也就是混合使用 RDB 和 AOF 两种机制的方法:就是让 RDB 快照以一定频率执行,在两次快照之间使用 AOF 日志记录这期间的所有命令操作。
如上图所示,第一次全量快照后,在T1 和 T2 时刻修改用 AOF 日志记录,等到第二次全量快照时,可以清空 AOF 日志。因为此时的修改都已经记录到快照中,恢复时也就不再使用日志了。
所以 RDB 和 AOF 混合机制综合了上述两种机制的优缺点。
AOF 的载入和还原过程在 AOF 小节中有提到。现在谈谈同时存在两者的情况,服务器如何用哪个文件来还原数据库状态:
《redis 设计与实现》
https://baijiahao.baidu.com/s?id=1654694618189745916&wfr=spider&for=pc
我是 python 的新手。我试图找到我的文本的频率分布。这是代码, import nltk nltk.download() import os os.getcwd() text_file=open(
我对安卓 fragment 感到困惑。我知道内存 fragment 但无法理解什么是 android fragment 问题。虽然我发现很多定义,比如 Android fragmentation re
尝试对 WordPress 进行 dockerise 我发现了这个场景: 2个数据卷容器,一个用于数据库(bbdd),另一个用于wordpress文件(wordpress): sudo docker
这个问题已经有答案了: From the server is there a way to know that my page is being loaded in an Iframe (1 个回答)
我正在玩小型服务器,试图对运行在其上的服务进行docker化。为简化起见,假设我必须主要处理:Wordpress和另一项服务。 在Docker集线器上有许多用于Wordpress的图像,但是它们似乎都
我想要发生的是,当帐户成功创建后,提交的表单应该消失,并且应该出现一条消息(取决于注册的状态)。 如果成功,他们应该会看到一个简单的“谢谢。请检查您的电子邮件。” 如果不是,那么他们应该会看到一条适当
就是这样,我需要为客户添加一个唯一标识符。通过 strip 元数据。这就是我现在完全构建它的方式,但是我只有最后一部分告诉我用户购买了哪个包。 我试着看这里: Plans to stripe 代码在这
我有一个类将执行一些复杂的操作,涉及像这样的一些计算: public class ComplexAction { public void someAction(String parameter
这个问题已经有答案了: maven add a local classes directory to module's classpath (1 个回答) 已关闭10 年前。 我有一些不应更改的旧 E
我使用 fragment 已经有一段时间了,但我经常遇到一个让我烦恼的问题。 fragment 有时会相互吸引。现在,我设法为此隔离了一个用例,它是这样的: Add fragment A(也使用 ad
我的 html 中有一个 ol 列表,上面有行条纹。看起来行条纹是从数字后面开始的。有没有办法让行条纹从数字开始? 我已经包含了正在发生的事情的片段 h4:nth-child(even) {
如何仅使用 css 将附加图像 html 化? 如果用纯 css 做不到,那我怎么能至少用一个图像来做 最佳答案 这不是真正的问题,而是您希望我们为您编写代码。我建议您搜索“css breadcrum
以下是 Joshua 的 Effective Java 的摘录: If you do synchronize your class internally, you can use various te
在这里工作时,我们有一个框向业务合作伙伴提供 XML 提要。对我们的提要的请求是通过指定查询字符串参数和值来定制的。其中一些参数是必需的,但很多不是。 例如,我们要求所有请求都指定一个 GUID 来标
我有 3 个缓冲区,其中包含在 32 位处理器上运行的 R、G、B 位数据。 我需要按以下方式组合三个字节: R[0] = 0b r1r2r3r4r5r6r7r8 G[0] = 0b g1g2g3g4
我最近发现了关于如何使用 History.js、jQuery 和 ScrollTo 通过 HTML5 History API 对网站进行 Ajax 化的要点:https://github.com/br
我们有一个 Spring Boot 应用程序,由于集成需要,它变得越来越复杂——比如在你这样做之后发送一封电子邮件,或者在你之后广播一条 jms 消息等等。在寻找一些更高级别的抽象时,我遇到了 apa
我正在尝试首次实施Google Pay。我面临如何指定gateway和gatewayMarchantId的挑战。 我所拥有的是google console帐户,不知道在哪里可以找到此信息。 priva
昨天下午 3 点左右,我为两个想要从一个 Azure 帐户转移到另一个帐户的网站设置了 awverify 记录。到当天结束时,Azure 仍然不允许我添加域,所以我赌了一把,将域和 www 子域重新指
我正在使用terms facet在elasticsearch服务器中获取顶级terms。现在,我的标签"indian-government"不被视为一个标签。将其视为"indian" "governm
我是一名优秀的程序员,十分优秀!