1、RDB工作原理和优缺点
工作中用到了redis的持久化,这里就开始总结了一下redis的持久化原理。
RDB是redis将内存数据存储到磁盘文件的其中一种方式(另一种是AOF)。我们可以在redis.conf文件中,做一些针对RDB的设置。参考“redis 配置文件 snapshotting RDB数据库快照部分”,有中英文的对照翻译。
################################ 快照 #################################
#
# Save the DB on disk:保存数据库到磁盘
#
# save <秒> <更新>
#
# 如果指定的秒数和数据库写操作次数都满足了就将数据库保存。
#
# 下面是保存操作的实例:
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
#
# 注释:注释掉“save”这一行配置项就可以让保存数据库功能失效。
#
# 你也可以通过增加一个只有一个空字符串的配置项(如下面的实例)来去掉前面的“save”配置。
#
# save ""
save 900 1
save 300 10
save 60 10000
#在默认情况下,如果RDB快照持久化操作被激活(至少一个条件被激活)并且持久化操作失败,Redis则会停止接受更新操作。
#这样会让用户了解到数据没有被正确的存储到磁盘上。否则没人会注意到这个问题,可能会造成灾难。
#
#如果后台存储(持久化)操作进程再次工作,Redis会自动允许更新操作。
#
#然而,如果你已经恰当的配置了对Redis服务器的监视和备份,你也许想关掉这项功能。
#如此一来即使后台保存操作出错,redis也仍然可以继续像平常一样工作。
stop-writes-on-bgsave-error yes
#是否在导出.rdb数据库文件的时候采用LZF压缩字符串和对象?
#默认情况下总是设置成‘yes’, 他看起来是一把双刃剑。
#如果你想在存储的子进程中节省一些CPU就设置成'no',
#但是这样如果你的kye/value是可压缩的,你的到处数据接就会很大。
rdbcompression yes
#从版本RDB版本5开始,一个CRC64的校验就被放在了文件末尾。
#这会让格式更加耐攻击,但是当存储或者加载rbd文件的时候会有一个10%左右的性能下降,
#所以,为了达到性能的最大化,你可以关掉这个配置项。
#
#没有校验的RDB文件会有一个0校验位,来告诉加载代码跳过校验检查。
rdbchecksum yes
# 导出数据库的文件名称
dbfilename dump.rdb
# 工作目录
#
# 导出的数据库会被写入这个目录,文件名就是上面'dbfilename'配置项指定的文件名。
#
# 只增的文件也会在这个目录创建(这句话没看明白)
#
# 注意你一定要在这个配置一个工作目录,而不是文件名称。
dir ./
上面的配置是很简单的,现在主要了解下RDB的工作机制和优缺点。当条件满足,redis需要执行RDB的时候,服务器会执行以下操作:
1. redis调用系统函数fork() ,创建一个子进程。
2.子进程将数据集写入到一个临时 RDB 文件中。
3.当子进程完成对临时RDB文件的写入时,redis 用新的临时RDB 文件替换原来的RDB 文件,并删除旧 RDB 文件。
在执行fork的时候操作系统(类Unix操作系统)会使用写时复制(copy-on-write)策略,即fork函数发生的一刻父子进程共享同一内存数据,当父进程要更改其中某片数据时(如执行一个写命令 ),操作系统会将该片数据复制一份以保证子进程的数据不受影响,所以新的RDB文件存储的是执行fork那一刻的内存数据。
Redis在进行快照的过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候RDB文件都是完整的。这使得我们可以通过定时备份RDB文件来实 现Redis数据库备份。RDB文件是经过压缩(可以配置rdbcompression参数以禁用压缩节省CPU占用)的二进制格式,所以占用的空间会小于内存中的数据大小,更加利于传输。
除了自动快照,还可以手动发送SAVE或BGSAVE命令让Redis执行快照,两个命令的区别在于,前者是由主进程进行快照操作,会阻塞住其他请求,后者会通过fork子进程进行快照操作。
Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将一个记录一千万个字符串类型键、大小为1GB的快照文件载入到内 存中需要花费20~30秒钟。
通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。
RDB的优点是:
1.RDB是一个非常紧凑(compact)的文件,它保存了redis 在某个时间点上的数据集。这种文件非常适合用于进行备份
和灾难恢复。
2.生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
3.RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
RDB缺点:
1.如果你需要尽量避免在服务器故障时丢失数据,那么RDB 不适合你。 虽然Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
2.每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。
2、AOF的工作原理与优缺点
在执行写命令时,AOF持久化会将执行的写命令也写到AOF文件的末尾,以此来记录数据的变化。换句话说,将AOF文件中包含的内容重新执行一遍,就可以回复AOF文件所记录的数据集。
在Redis.conf配置中设置如下:
# redis默认关闭AOF机制,可以将no改成yes实现AOF持久化
appendonly no
# AOF文件
appendfilename "appendonly.aof"
# AOF持久化同步频率,always表示每个Redis写命令都要同步fsync写入到磁盘中,但是这种方式会严重降低redis的速度;everysec表示每秒执行一次同步fsync,显示的将多个写命令同步到磁盘中;no表示让操作系统来决定应该何时进行同步fsync,Linux系统往往可能30秒才会执行一次
# appendfsync always
appendfsync everysec
# appendfsync no
# 在日志进行BGREWRITEAOF时,如果设置为yes表示新写操作不进行同步fsync,只是暂存在缓冲区里,避免造成磁盘IO操作冲突,等重写完成后在写入。redis中默认为no
no-appendfsync-on-rewrite no
# 当前AOF文件大小是上次日志重写时的AOF文件大小两倍时,发生BGREWRITEAOF操作。
auto-aof-rewrite-percentage 100
#当前AOF文件执行BGREWRITEAOF命令的最小值,避免刚开始启动Reids时由于文件尺寸较小导致频繁的BGREWRITEAOF。
auto-aof-rewrite-min-size 64mb
# Redis再恢复时,忽略最后一条可能存在问题的指令(因为最后一条指令可能存在问题,比如写一半时突然断电了)
aof-load-truncated yes
#Redis4.0新增RDB-AOF混合持久化格式,在开启了这个功能之后,AOF重写产生的文件将同时包含RDB格式的内容和AOF格式的内容,其中RDB格式的内容用于记录已有的数据,而AOF格式的内存则用于记录最近发生了变化的数据,这样Redis就可以同时兼有RDB持久化和AOF持久化的优点(既能够快速地生成重写文件,也能够在出现问题时,快速地载入数据)。
aof-use-rdb-preamble no
关于redis的刷新模式和日志重写:
由于写操作通常是有缓冲的,所以有可能AOF操作并没有写到硬盘中,一般可以通过fsync()来强制输出到硬盘中。而fsync()的频率可以通过配置文件中的flush策略来指定,可以选择每次事件循环写操作都强制fsync或者每秒fsync至少运行一次。
当rewrite子进程开始后,父进程接受到的命令会添加到aof_rewrite_buf_blocks中,使得rewrite成功后,将这些命令添加到新文件中。在rewrite过程中,原来的AOF也可以选择是不是继续添加,由于存在性能上的问题,在rewrite过程中,如果fsync()继续执行,会导致IO性能受损影响Redis性能。所以一般情况下rewrite期间禁止fsync()到旧AOF文件。这策略可以在配置文件中修改。
原理如图所示:
1、AOF持久化机制的优点
(1)AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据
(2)AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复
(3)AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在rewrite log的时候,会对其中的指导进行压缩,创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,再交换新老日志文件即可。
(4)AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
2、AOF持久化机制的缺点
(1)对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大
(2)AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件,当然,每秒一次fsync,性能也还是很高的
(3)以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似AOF这种较为复杂的基于命令日志/merge/回放的方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。
3、RDB和AOF到底该如何选择
(1)不要仅仅使用RDB,因为那样会导致你丢失很多数据
(2)也不要仅仅使用AOF,因为那样有两个问题,第一,你通过AOF做冷备,没有RDB做冷备,来的恢复速度更快; 第二,RDB每次简单粗暴生成数据快照,更加健壮,可以避免AOF这种复杂的备份和恢复机制的bug
(3)综合使用AOF和RDB两种持久化机制,用AOF来保证数据不丢失,作为数据恢复的第一选择; 用RDB来做不同程度的冷备,在AOF文件都丢失或损坏不可用的时候,还可以使用RDB来进行快速的数据恢复