Redis如何保证数据不丢失?

AOF

AOF全程AppendOnlyFile,是Redis崩溃恢复的机制。Redis根据配置在适当的时机将写操作的日志记录到AOF磁盘文件中。当Redis崩溃重启时,依次执行AOF中的操作即可恢复数据。AOF有点类似于MySQL的redolog,都是在再数据写入内存之后,将对应的修改操作日志记录下来,只不过MySQL的redolog还涉及到与binlog的二阶段提交来确保事务的数据一致性。

AOF内容格式

在AOF中并不是将Redis操作指令字符写入到文件中,而是将指令格式化后写入文件。每一个AOF日志记录以*数字开头,数字表示改操作由几个部分组成。每个部分由$+数字+内容组成,每个数字表示内容的字节大小,以set testkey testvalue指令为例子:那么在AOF中*3$3set$7testkey$9testvalue。

AOF持久化

AOF中的记录需要持久化到AOF磁盘文件中,否则Redis发生崩溃重启,那么在内存中的AOF记录是无法恢复的,也就无法用来恢复Redis的数据。但是如果每次操作之后将记录写入磁盘文件中,那么会降低Redis的操作效率(因为redis只有一个线程在执行写操作,所以不会影响当前写操作,但是会影响下一个写操作)。Redis提供了三种配置:

1) Always ,同步写回:每个写命令执行完,立马同步地将日志写回磁盘,此操作由Reids主线程去执行

2)Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘,此操作由专门的同步线程处理

3)No,操作系统控制的写回:每个写命令执行完,只是把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘,此操作完全由操作系统处理

这三种配置可以通过配置appendfsync来选择。不同的配置倾向于不同的数据可靠性、写操作性能。

Alwayns可以做到基本不丢数据,但是它在每一个写命令后都有一个慢速的落盘操作,不可避免地会影响主线程性能;

Everysec:每秒写,性能适中,数据完可靠性也适中。

No:完全将落盘操作交给操作系统,性能最好,但是数据可靠性最差。

AOF重写

Redis引入了AOF来解决崩溃恢复的问题,但是当AOF文件过大就会出现新的问题,

1)文件过大,落盘操作很慢,甚至无法写入(超出文件系统限制)

2)崩溃恢复需要依次执行AOF中的指令,AOF过大,那么恢复时间也会很慢。

类似于MySQL中Redolog写满时会进行刷盘(Redolog日志写满会清空内容,然后将对应的脏页回写到磁盘中)操作,Redis也会给AOF文件进行类似的瘦身的操作,即使AOF重写。整体思路是当AOF超过配置阈值时,将Redis内存中的数据复制一份出来,由后台子进程将Redis中的数据依次拷贝成写入操作,然后将新的写入操作记录到新的AOF文件中。比如 键值testkey在业务系统中依次被set为1,2,3。那么重写后的AOF实际上只需要有set testkey 3操作即可。按照整个思路就可以将AOF进行瘦身。在所有数据拷贝操作完毕之前,Redis的写操作也不会受到任何影响。Reids每次在内存中执行完写操作之后,依旧将操作日志写入到旧的AOF缓存内存中(由同步机制来落盘),且将相同的操作记录记录到AOF重写日志缓存区中。当瘦身操作完成之后,会将AOF重写日志的记录写入到新的AOF文件中,然后用新的AOF文件替换旧的AOF文件,最终实现瘦身的目的。总体来说,每次 AOF 重写时,Redis 会先执行一个内存拷贝,用于重写。然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为 Redis 采用额外的线程进行数据重写,所以,这个过程并不会阻塞主线程。

Redis 主线程 fork 创建 后台子进程时,内核需要创建用于管理子进程的相关数据结构,这些数据结构在操作系统中通常叫作进程控制块(Process Control Block,简称为 PCB)。内核要把主线程的 PCB 内容拷贝给子进程。这个创建和拷贝过程由内核执行,是会阻塞主线程的。而且,在拷贝过程中,子进程要拷贝父进程的页表,这个过程的耗时和 Redis 实例的内存大小有关。如果 Redis 实例内存大,页表就会大,fork 执行时间就会长,这就会给主线程带来阻塞风险。总之就是内存越大,那么fork操作就越慢,影响正常的读写业务。

RDB

AOF恢复数据时需要将文件中的指令全部执行一遍,此操作耗时还是过长。Redis通过RedisDataBase(RDB)将内存中的数据写入到磁盘中。当需要崩溃恢复时,只需要将RDB文件中的数据直接加载到内存中,这样就避免了AOF中的指令执行过慢的问题。

Redis提供了两个指令来备份数据:

1)save 该操作在redis主线程中执行,会堵塞正常的Redis读写操作。

2)bgsave 该操作在子进程中执行。执行bgsave时,首先主线程fork出子进程来执行备份操作。在备份过程中,Redis通过操作系统的写时复制来避免对主线产生堵塞。大概思路是:fork的子进程与主线程会共享内存,因此bgsave操作并不会影响的主线程的读操作。当主线程需要进行写操作时,那么主线程会将对应的内存块复制出一份进行写操作,这样bgsave也不会影响主线的写操作。

虽然bgsave操作不会对主线程有堵塞,但是主线程fork子进程的操作也需要占用一些时间,而且fork子进程会使得进程内存上涨。

通过fork创建子进程会将主进程的页表复制一份给子进程,因此主进程的占用的内存容量越大,那么复制页表也就越慢。然后bgsave进程就可以根据页表来访问内存的kv数据,生成rbd文件。

最佳实践

RDB是将内存中的数据去全量写入磁盘中,该操作耗时会比记录一条AOF操作日志要长很多。因此当Redis故障恢复时,只能恢复到上一次备份时的状态。上次备份到Redis崩溃重启间的数据是会丢失的,这对业务也是难以接受的。如果缩短RDB的备份间隔,甚至让Redis每时每刻都在进行备份,从理论上可以解决数据丢失的问题。但是主线程fork子进程操作也会消耗资源。不仅如此,大量的子进程在进行备份也会出给磁盘造成很大的压力。

如果将RDB作为定期全量备份,再加上AOF的增量备份。每次开始新的全量备份时都会清理掉就的AOF日志。当崩溃恢复时,全量备份(RDB)能够很快的恢复数据,然后执行少量的增量备份(AOF)操作记录就能解决数据丢失问题,还避免了频繁全量备份的带来的诸多问题。

AOF+RDB的组合有点类似于MySQL中的Binlog+Redolog。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值