第十章Redis持久化

10.1 Redis持久化操作概述
Redis是一个功能强大、读/写速度极快、性能优越的数据库。它的性能之所以强大在很大程度上是因为它将所有数据存储在内存中,使得读/写速度及相关性能得到很大的提升。但当Redis进程退出或者重启后,所有存储在内存中的数据就会丢失。
在实际应用中希望Redis在重启后可以保证数据不丢失,比如:
●利用Redis作为数据库存储数据,长久保存数据
●利用Redis作为缓存服务器缓存大量数据。但当缓存被穿透后,会对其性能造成较大影响。更严重的是当所有缓存同时失效时会导致缓存雪崩,从而使得服务器停止服务。
为了解决Redis服务器重启后数据丢失的问题,Redis采用某种方式将数据从内存保存到硬盘中,使得服务器重启之后Redis可以根据硬盘中保存的数据进行恢复,这个过程就是持久化,过程产生的文件就是持久化文件。利用Redis的持久化文件就能实现数据恢复,从而达到保存数据不丢失的目的。
Redis支持两种持久化方式:AOF持久化和RDB持久化。AOF持久化方式会将每次执行的命令及时保存到硬盘中;RDB持久化方式会根据指定的规则“定时”将内存中的数据保存到硬盘中。AOF 持久化方式的实时性更好,也就是当进程意外退出时,丢失的数据更少。在通常情况下,这两种持久化方式可以单独使用,但在更多情况下,可以将二者结合起来使用。
10.2 Redis持久化机制AOF
AOF(Append Only File)持久化保存服务器执行的所有写操作命令到单独的日志文件中,在服务器重启时通过加载日志文件中的命令并执行来恢复数据。这个日志文件就是 AOF文件,Redis以Redis协议格式来保存AOF文件中的所有命令,新命令会被追加到文件的结尾。在服务器的后台,AOF文件还会被重写(Rewrite),使AOF文件的体积不会大于保存数据集状态所需的实际大小。
当使用Redis来存储一些需要长久保存的数据时,一般需要打开AOF持久化来降低进程突然中止导致数据丢失的风险。
10.2.1 AOF持久化的配置
默认情况下AOF持久化没有开启,如果要采用AOF持久化方式保存数据要开启AOF 持久化。可以通过修改配置文件redis.conf中的appendonly参数开启:
appendonly yes
开启AOF持久化后,服务器每执行一条写命令,Redis就会把该命令写入硬盘的AOF 文件中。AOF文件位置可以通过dir参数来设置。AOF文件的默认名称是appendonly.aof,可以通过appendfilename参数来修改AOF文件的名称:
appendfilename "appendonly.aof"
与AOF持久化相关的配置总结如下:
●appendonly no:是否开启AOF持久化,默认为no(不开启),设置为yes表示开启
AOF持久化
●appendfilename "appendonly.aof":AOF文件名,可以修改它
●dir ./:AOF文件和RDB文件所在目录
●appendfsync everysec:fsync持久化策略
●no-appendfsync-on-rewrite no:在重写AOF文件的过程中是否禁止fsync。如果这个参数值设置为yes(开启)则可以减轻重写AOF文件时CPU和硬盘的负载,但同时可能会丢失重写AOF文件过程中的数据,需要在负载与安全性之间进行平衡
●auto-aof-rewrite-percentage 100:指定Redis重写AOF文件的条件,默认为100,它会对比上次生成的AOF文件大小。如果当前AOF文件的增长量大于上次AOF文件的 100%就会触发重写操作,如果将该选项设置为0则不会触发重写操作
●auto-aof-rewrite-min-size 64mb:指定触发重写操作的AOF文件的大小,默认为64MB。如果当前AOF文件的大小低于该值,此时就算当前文件的增量比例达到了auto-aof-rewrite-percentage选项所设置的条件,也不会触发重写操作。换句话说,只有同时满足以上这两个选项所设置的条件才会触发重写操作
●auto-load-truncated yes:当AOF文件结尾遭到损坏,Redis在启动时是否仍加载
AOF文件
10.1.2 AOF持久化的实现
在开启AOF持久化之后,Redis服务器每执行一条写命令,AOF文件都会记录这条写命令。因为需要实时记录Redis的每条写命令,因此AOF不需要触发就能实现持久化。
AOF持久化的实现过程如下:
(1)命令追加(append):Redis服务器每执行一条写命令,这条写命令都会被追加到缓存区aof_buf中。
在追加命令的过程中,Redis并没有直接将命令写入文件中,而是将命令追加到缓存区aof_buf的末尾。这样做的目的是避免每次执行的命令都直接写入硬盘中,会导致硬盘 I/O的负载过大,使性能下降。命令追加的格式使用Redis命令请求的协议格式,它是一种纯文本格式,具有很多优点,如兼容性好、易处理、易读取、操作简单、可避免二次开销等。比如,执行以下命令:
127.0.0.1:6379>SET name redis
OK
服务器在接收到客户端发送过来的SET命令后,会将下面的协议格式内容追加到缓存区aof_buf的末尾:
*3\r\n3\r\nname\r\n$5\r\nredis\r\n
在AOF文件中,除了用于切换数据库的select命令是由Redis添加的,其他写命令都是客户端发送过来的。
(2)AOF持久化文件写入(write)和文件同步(sync):根据appendfsync参数设置的不
同的同步策略,将缓存区aof_buf中的数据内容同步到硬盘中。
Redis为AOF缓存区的同步提供了多种策略,策略涉及操作系统的write和fsync函数。为提高文件的写入效率,当用户调用write函数将数据写入文件中时,操作系统会将这些数据暂存到一个内存缓存区中,当这个缓存区被填满或者超过了指定时限后才会将缓存区中的数据写入硬盘中,这样做既提高了效率又保证了安全性。
Redis的服务器进程是一个事件循环(loop),这个事件循环中的文件事件负责接收客户端的命令请求,处理之后向客户端发送命令回复,其中的时间事件则负责执行像 serverCron函数这样需要定时运行的函数。
服务器在处理文件事件时,可能会执行客户端发送过来的写命令,使得一些命令被追加到缓存区aof_buf中。因此在服务器每次结束一个事件循环之前都会调用flushAppendOnlyFile函数来决定是否将缓存区aof_buf中的数据写入和保存到AOF文件中。
flushAppendOnlyFile函数的运行与服务器配置的appendfsync参数有关。appendfsync 参数具有多个值,具体如下:
●当appendfsync参数的值为always时,flushAppendOnlyFile函数会将缓存区aof_buf 中的所有内容写入并同步到AOF文件中。服务器的文件事件每循环一次,都要将缓存区aof_buf中的所有内容写入AOF文件中并同步AOF文件,这个过程在无形中加大了硬盘I/O的负载,使得硬盘I/O成为性能瓶颈,从而严重降低Redis的性能。所以使用always的效率比较低,但从安全性考虑,使用always是安全的。即使Redis 服务器出现故障,AOF持久化也只会丢失近一次事件循环中的命令数据。
●当appendfsync参数的值为no时,flushAppendOnlyFile函数会将缓存区aof_buf中的所有内容写入AOF文件中,但不会同步AOF文件,至于什么时候同步则交给操作系统来决定,通常同步周期为30秒。在使用no时,AOF文件的同步不可控且缓存区中的内容会越来越多,一旦发生故障将会丢失大量数据。因为不用执行AOF同步操作,所以AOF写入数据的速度总是快的,效率也很高。
●当appendfsync参数的值为everysec时,flushAppendOnlyFile函数会将缓存区aof_buf 中的所有内容写入AOF文件中。而AOF文件的同步操作则由一个专门的文件同步线程负责,每秒执行一次。如果上次同步AOF文件的时间距离现在超过了1秒,同步线程就会再次对AOF文件进行同步。在使用everysec时,AOF文件的写入与同步效率非常高,它是前面两种策略的折中,是性能和数据安全性的平衡,既满足了效率要求又考虑了安全性,推荐使用。
10.1.3 AOF文件重写
1.AOF文件重写的目的
定期重写AOF文件以达到压缩的目的。AOF持久化的实现是通过保存被执行的写命令来保存数据库数据的。随着服务器运行时间的增加,AOF文件的内容数据会越来越大,文件所占据的内存也会变大。过大的AOF文件会影响服务器的正常运行,在执行数据恢复时将会耗费更多的时间。
为解决AOF文件体积过大的问题,Redis提供了AOF文件重写的功能,就是定期重写 AOF文件以减小AOF文件的体积。其实AOF文件重写就是把Redis进程内的数据转化为写命令然后同步到新的AOF文件中。在重写的过程中Redis服务器会创建一个新的AOF文件来替代现有的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但新的AOF文件不会包含冗余命令。
Redis将生成新的AOF文件替换旧的AOF文件的功能命名为AOF文件重写。实际上,AOF文件重写并不会对旧的AOF文件进行读取、写入操作,这个功能是通过读取服务器当前的数据库状态来实现的。
通过客户端向服务器端发送多条 RPUSH 命令,向列表中添加多个颜色元素,并成功执行。操作如下:
127.0.0.1:6379> RPUSH color "red" "blue" #向列表color中添加多个颜色元素 (integer) 2
127.0.0.1:6379> RPUSH color "yellow" "green" "black" (integer) 5
127.0.0.1:6379> LPOP color #移除并返回列表color的头元素 "red"
127.0.0.1:6379> LPOP color
"blue"
127.0.0.1:6379> RPUSH color "pink" "white" (integer) 5
Redis服务器在开启了AOF持久化之后,就会保持当前列表color键的状态,在AOF文件中写入5条命令。如果服务器想用少的命令来保存列表color键的状态,就要利用AOF 文件重写功能。简单的方法不是去读取和分析现有AOF文件的内容,而是直接从数据库中读取出列表color键的值,用RPUSH color "yellow" "green" "black" "pink" "white"命令来代替保存在AOF中的5条命令,这样就实现了AOF文件重写功能。
除上面所说的列表键之外,其他类型的键也可以用同样的方法去减少AOF文件中的命令数量,也就是直接去数据库中读取该键所存储的值,然后用一条命令记录来代替之前这个键值对的多条写命令,这就是AOF文件重写功能的原理。
为什么AOF文件重写可以压缩AOF文件?原因有如下几点:
● AOF文件重写功能会丢弃过期的数据,也就是过期的数据不会被写入AOF文件中
● AOF文件重写功能会丢弃无效的命令,无效的命令将不会被写入AOF文件中。无效命令包括重复设置某个键值对时的命令、删除某些数据时的命令等
● AOF文件重写功能可以将多条命令合并为一条命令,然后写入AOF文件中
在实际应用中,Redis为了防止在执行命令时造成客户端缓存区溢出,重写程序在处理列表、哈希表、集合及有序集合这4种可能会带有多个元素的键时,会提前检查这些键所包含的元素个数。假如键所包含的元素个数大于redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值,那么重写程序会使用多条命令来记录这个键的值。REDIS_AOF_REWRITE_ITEMS_PER_CMD 常量的值为 64。
2.AOF文件重写的触发方式
AOF文件重写的触发有两种方式:手动触发和自动触发。
l .手动触发:执行BGREWRITEAOF命令触发AOF文件重写。该命令与BGSAVE命令相似,都是启动(fork)子进程完成具体的工作且都在启动时阻塞。如图所示为执行BGREWRITEAOF命令触发AOF文件重写。

图片1.png

自动触发:自动触发AOF文件重写是通过设置Redis配置文件中auto-aof-rewrite-
percentage和auto-aof-rewrite-min-size参数的值,以及aof_current_size和 aof_base_size状态来确定何时触发的。
auto-aof-rewrite-percentage参数是在执行AOF文件重写时,当前AOF文件的大小
(aof_current_size)和上一次AOF文件重写时的大小(aof_base_size)的比值,默认为 100。 auto-aof-rewrite-min-size参数设置了执行AOF文件重写时的最小体积,默认为 64MB。
使用 CONFIG GET 命令来查看上述参数的值,操作如下:
127.0.0.1:6379> CONFIG GET auto-aof-rewrite-percentage
1)"auto-aof-rewrite-percentage"
2)"100"
127.0.0.1:6379> CONFIG GET auto-aof-rewrite-min-size
1)"auto-aof-rewrite-min-size"
2)"67108864"
使用INFO PERSISTENCE命令来查看AOF持久化的相关状态,操作如下:
127.0.0.1:6379> INFO PERSISTENCE

Persistence

… aof_enabled:0 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:0 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_cow_size:184320
只有当Redis服务器同时满足auto-aof-rewrite-percentage和auto-aof-rewrite-min-size参数值时才会触发AOF文件重写。
3.AOF文件后台重写
在实现AOF文件重写的过程中,会调用aof_rewrite函数创建一个新的AOF文件,同时将旧的AOF文件的命令重写到新的AOF文件中,在这个过程中会执行大量的写入操作会使得这个函数的线程被长时间阻塞。Redis服务器使用单线程来处理命令请求。如果让服务器直接调用aof_rewrite重写函数,那么在AOF文件重写期间服务器将不能继续执行其他命令会一直处于阻塞状态。因此,Redis将AOF文件重写程序放到了一个子进程中执行,这样做的好处是:
●子进程在执行AOF文件重写的过程中,Redis服务器进程可以继续处理新的命令请求。
●子进程带有服务器进程的数据副本,使用子进程可以在使用锁的情况下,保证数据的安全性。
使用子进程会导致数据库状态不一致,原因是:当子进程进行AOF文件重写的时候, Redis服务器可以继续执行来自客户端的命令请求,就会有新的命令对现有数据库状态进行修改,进而使得服务器当前的数据库状态与重写的AOF文件所保存的数据库状态不一致。
为解决使用子进程导致数据库状态不一致的问题,Redis服务器设置了一个AOF文件重写缓存区。这个AOF文件重写缓存区在服务器创建子进程之后开始使用,可以利用它来解决数据库状态不一致的问题。当Redis服务器成功执行完一条写命令后,它会同时将这条写命令发送给AOF文件缓存区(aof_buf)和AOF文件重写缓存区。子进程在执行AOF 文件重写的过程中,服务器进程的执行过程如下:
(1)服务器接收到来自客户端的命令请求,并成功执行。
(2)服务器将执行后的写命令转化为对应的协议格式,然后追加到 AOF 文件缓存区
(aof_buf)中。
(3)服务器再将执行后的写命令追加到 AOF 文件重写缓存区中。
以上过程用流程图表示如图所示:

图片.png

有了AOF文件重写缓存区,就可以保证数据库状态的一致性。AOF文件缓存区的内容会被定期写入和同步到AOF文件中,AOF文件的写入和同步不会因为AOF文件重写缓存区的引入而受到影响。当服务器创建子进程之后,服务器执行的所有写命令都会同时被追加到AOF文件缓存区和AOF文件重写缓存区中。
如果子进程完成了AOF文件重写的工作,它就会发送一个完成信号给父进程。当父进程接收到这个信号后,就会调用信号处理函数继续执行以下工作:
(1)将AOF文件重写缓存区中的所有内容写入新的AOF文件中。新的AOF文件所保存的数据库状态与服务器当前的数据库状态保持一致
(2)修改新的AOF文件的文件名,新生成的AOF文件将会覆盖现有(旧)的AOF 文件,完成新旧两个文件的互换
在完成上述两个步骤之后,就完成了一次AOF文件后台重写工作。 在整个AOF文件
后台重写的过程中,只有在信号处理函数执行的过程中,服务器进程才会被阻塞,在其他时候不存在阻塞情况。
10.1.4 AOF文件处理
如果开启了AOF持久化,那么在Redis服务器启动的时候就会先加载AOF文件中的数据来恢复数据库数据。因为AOF文件中保存了数据库状态所需的所有写命令,所以服务器读取并执行AOF文件中的写命令,就可以还原服务器关闭之前的数据库状态。这个过程具体如下:
(1)创建一个伪客户端,用于执行AOF文件中的写命令。这个伪客户端是一个不带网络连接的客户端。因为只能在客户端的上下文中才能执行Redis的命令,而在AOF 文件中包含了Redis服务器启动加载AOF文件时所使用的所有命令而不是网络连接,所以服务器创建了一个不带网络连接的伪客户端来执行AOF文件中的写命令
(2)读取AOF文件中的数据,分析并提取出AOF文件所保存的一条写命令
(3)使用伪客户端执行被读取出的写命令
(4)重复执行步骤(2)和(3)直到将AOF文件中的所有命令读取完毕并成功执行为止。这个过程完成之后,就可以将服务器的数据库状态还原为关闭之前的状态。
Redis启动加载AOF文件恢复数据的过程如图所示:
图片.png

如果在Redis服务器启动加载AOF文件时发现AOF文件被损坏,那么服务器会拒绝加载这个AOF文件,以此来确保数据的一致性不被破坏。而AOF文件被损坏的原因可能是程序正在对AOF文件进行写入与同步时服务器出现停机故障。如果AOF文件被损坏了,则可以通过以下方法来修复:
●及时备份现有AOF文件
●利用Redis自带的redis-check-aof程序,对原来的AOF文件进行修复,命令如下:
$ redis-check-aof –fix
●使用diff -u来对比原始AOF文件和修复后的AOF文件,找出这两个文件的不同之处
●修复AOF文件之后,重启Redis服务器重新加载,进行数据恢复
10.1.5 AOF持久化的优劣
AOF持久化具有以下优点:
●使用AOF持久化会让Redis持久化更长:通过设置不同的fsync策略来达到更长的
持久化。具体有 3 种策略。
当有新的写命令追加到AOF文件末尾时,就执行一次 fsync。这种方式虽然速度比较慢,但是很安全。
设置为每秒执行一次fsync。这种方式速度比较快,如果发生故障,则只会丢失 1 秒内的数据,即兼顾了效率与安全性,推荐使用。
从不执行fsync,而是直接将数据交给操作系统来处理。这种方式虽然速度比较快,但是安全性比较差,不建议使用。
●兼容性比较好:AOF文件是一个日志文件,它的作用是记录服务器执行的所有写命令。当文件因为某条写命令写入失败时,可以使用 redis-check-aof 进行修复,然后继续使用。
●支持后台重写:当 AOF 文件的体积过大时,在后台可以自动地对 AOF 文件进行重写,因此数据库当前状态的所有命令集合都会被重写到 AOF 文件中。重写完成后,
Redis 就会切换到新的 AOF 文件,继续执行写命令的追加操作。
●AOF 文件易于读取和加载:AOF 文件保存了对数据库的所有写命令,这些写命令采用 Redis 协议格式追加到 AOF 文件中,因此非常容易读取和加载。
AOF 持久化具有以下缺点:
●AOF 文件的体积会随着时间的推移逐渐变大,导致在加载时速度会比较慢,进而影响数据库状态的恢复速度,性能快速下降。
●根据所使用的 fsync 策略,使用 AOF 文件恢复数据的速度可能会慢于使用 RDB 文件恢复数据的速度。
●因为 AOF 文件的个别命令,可能会导致在加载时失败,从而无法进行数据恢复。
10.2 Redis持久化机制RDB
Redis持久化机制RDB与持久化机制AOF类似,都是为了避免Redis服务器在内存中的数据因为服务器进程的退出丢失而建立的一种持久化机制。RDB持久化生成的RDB文件是一个经过压缩的二进制文件,也可以称之为快照文件,通过该文件可以还原生成RDB 文件时的数据库状态。因为RDB文件保存在硬盘上,所以就算服务器停止服务,也可以利用RDB文件来还原数据库状态。
10.2.1 RDB持久化
在指定的时间间隔内,RDB持久化可以生成数据集的时间点快照。在指定的时间间隔内Redis会自动将内存中的所有数据生成一份副本并存储在硬盘上,这个过程就是“快照”。
1.快照处理的发生条件
当出现以下几种情况时,Redis会对数据进行快照处理:
●根据Redis配置文件redis.conf中的配置自动进行快照(自动触发)
在Redis中,用户可以根据实际需要自行定义快照条件,当符合快照条件时,服务器会自动执行快照操作。快照条件是在Redis配置文件redis.conf中设置的,用户可以自定义,格式为:save m n。它由两个参数构成:时间m和被修改的键的个数n。当在时间m内被修改的键的个数大于n时,就会触发BGSAVE命令,服务器就会自动执行快照操作。
Redis配置文件redis.conf中的默认设置如下:
save 900 1
save 300 10
save 60 10000
以上3个快照条件都是以save属性开头的,它们之间是“或”的关系,也就是每次只有其中一个快照条件会被执行。
save 900 1:表示在900秒内有1个或1个以上的键被修改就会进行快照处理
save 300 10:表示在300秒内有10个或10个以上的键被修改就会进行快照处理
save 60 1000:表示在60秒内有1000个或1000个以上的键被修改就会进行快照处理
Redis的save m n命令是通过serverCron函数、dirty计数器及lastsave时间戳来实现的。
serverCron函数:这是Redis服务器的周期性操作函数,默认每隔100毫秒执行一次,它主要的作用是维护服务器的状态。其中一项工作就是判断save m n配置的条件是否满足,如果满足就会触发执行BGSAVE命令。
dirty计数器:这是Redis服务器维持的一种状态,它主要用于记录上一次执行SAVE 或BGSAVE命令后,服务器进行了多少次状态修改(执行添加、删除、修改等操作);当 SAVE或BGSAVE命令执行完成后,服务器会将dirty重新设置为0。dirty计数器记录的是服务器进行了多少次状态修改,而不是客户端执行了多少次修改数据的命令。
lastsave时间戳:主要用于记录服务器上一次成功执行SAVE或BGSAVE命令的时间,它是Redis服务器维持的一种状态。
dirty计数器和lastsave时间戳属性都保存在服务器状态的redisServer结构中。
save m n命令的实现原理:服务器每隔100毫秒执行一次serverCron 函数,serverCron 函数会遍历save m n配置的保存条件判断是否满足。如果有一个条件满足,就会触发执行 BGSAVE命令进行快照保存。
对于每个save m n条件,只有以下两个条件同时满足才算满足:
当前服务器时间减去lastsave时间戳大于m
当前dirty计数器的个数大于等于n
●用户在客户端执行SAVE或BGSAVE命令时会触发快照(手动触发)。
●如果用户定义了自动快照条件则执行FLUSHALL命令也会触发快照。
当执行FLUSHALL 命令时,会清空数据库中的所有数据。如果用户定义了自动快照条件,则在使用FLUSHALL命令清空数据库的过程中,就会触发服务器执行一次快照。
●如果用户为Redis设置了主从复制模式,从节点执行全量复制操作,则主节点会执
行BGSAVE命令,将生产的RDB文件发送给从节点完成快照操作。
2.快照的实现过程
(1)Redis调用执行fork函数复制一份当前进程(父进程)的副本(子进程),也就是同时拥有父进程和子进程。
(2)父进程与子进程各自分工,父进程继续处理来自客户端的命令请求,而子进程则将内存中的数据写到硬盘上的一个临时RDB文件中。
(3)当子进程把所有数据写完后,也就表示快照生成完毕,此时旧的RDB文件将会被这个临时RDB文件替换,这个旧的RDB文件也会被删除。这个过程就是一次快照的实现过程。
当Redis调用执行fork函数时,操作系统会使用写时复制策略。也就是在执行fork函数的过程中,父、子进程共享同一内存数据,当父进程要修改某个数据时(执行一条写命令),操作系统会将这个共享内存数据另外复制一份给子进程使用,以此来保证子进程的正确运行。因此新的RDB文件存储的是执行fork函数过程中的内存数据。
写时复制策略也保证了在执行fork函数的过程中生成的两份内存副本在内存中的占用量不会增加一倍。但是在进行快照的过程中,如果写操作比较多就会造成fork函数执行前后数据差异较大,此时会使得内存使用量变大。因为内存中不仅保存了当前数据库数据,还会保存fork过程中的内存数据。
在进行快照生成的过程中,Redis不会修改RDB文件。只有当快照生成后,旧的RDB 文件才会被临时RDB文件替换,同时旧的RDB文件会被删除。在整个过程中,RDB文件是完整的,因此我们可以使用RDB文件来实现Redis数据库的备份。
10.2.2 RDB文件
在默认情况下,Redis将数据库快照保存在名为dump.rdb的文件中,这个文件被称为 RDB文件,它是一个经过压缩的二进制文件。使用RDB文件可以还原生成RDB文件的数据库状态,也可以备份数据库数据。
RDB文件的存储路径可以在启动前配置,也可以通过命令来直接修改。
配置文件配置:在Redis的配置文件redis.conf文件中,dir用于指定RDB文件、AOF 文件所在的目录,默认存放在Redis根目录下,dbfilename 用于指定文件名称。
命令修改:在Redis服务器启动后,也可以通过命令来修改RDB文件的存储路径,命令格式如下:
CONFIG SET dir {文件路径}
CONFIG SET dbfilename {新文件名}
RDB文件结构如图所示:
图片.png

在RDB文件结构中,通常使用大写字母表示常量,使用小写字母表示变量和数据。
RDB文件主要由图中的几个常量和变量组成,具体说明如下:
● REDIS 常量:该常量位于RDB文件的头部,它保存着“REDIS”5个字符,它的长度是5字节。在Redis服务器启动加载文件时,程序会根据这5个字符判断加载的文件是不是RDB文件。
● db_version常量:该常量用于记录RDB文件的版本号,它的值是一个用字符串表示的整数,占4字节,注意区分它不是 Redis 的版本号。
● databases 数据:它包含0个或多个数据库以及各个数据库中的键值对数据。
如果它包含0个数据库,也就是服务器的数据库状态为空,那么databases也是空的,其长度为0字节;如果它包含多个数据库,也就是服务器的数据库状态不为空,那么 databases不为空,根据它所保存的键值对的数量、类型和内容不同,其长度也是不一样的。
如果databases不为空,则RDB文件结构如图所示:
[图片上传失败...(image-49ee8d-1627917286542)]
其中,SELECTDB是一个常量,表示其后的数据库编号,这里的0和1是数据库编号。
pairs数据:它存储了具体的键值对信息,包括键(key)、值(value)、数据类型、内部编码、过期信息、压缩信息等。
SELECT 0 pairs表示0号数据库;SELECT 1 pairs表示1号数据库。当数据库中有键值对时,RDB文件才会记录该数据库的信息;而如果数据库中没有键值对,这一部分就会被RDB文件省略。
● EOF 常量:该常量是一个结束标志,它标志着 RDB 文件的正文内容结束,其长度为 1 字节。在加载 RDB 文件时,如果遇到 EOF 常量,则表示数据库中的所有键值对都已经加载完毕。
● check_sum 变量:该变量用于保存一个校验和,这个校验和是通过对 REDIS、 db_version、databases、EOF 4 部分的内容进行计算得出的,是一个无符号整数,其长度为 8 字节。
当服务器加载 RDB 文件时,会将 check_sum 变量中保存的校验和与加载数据时所计算出来的校验和进行比对,从而判断加载的 RDB 文件是否被损坏,或者是否有错误。
在默认情况下,Redis 服务器会自动对 RDB 文件进行压缩。在 Redis 配置文件 redis.conf 中,默认开启压缩。配置如下:
rdbcompression yes #默认为开启压缩
如果不想开启压缩,则可以将 yes 值改为 no。也可以通过命令来修改,命令格式如下:
CONFIG SET rdbcompression no
在默认情况下,Redis 采用 LZF 算法进行 RDB 文件压缩。在压缩 RDB 文件时,不要误认为是压缩整个 RDB 文件。实际上,对 RDB 文件的压缩只是针对数据库中的字符串进行的,并且只有当字符串达到一定长度(20 字节)时才会进行压缩。
10.2.3 RDB文件的创建与加载
RDB文件可以使用命令直接生成。在 Redis 中,有SAVE和BGSAVE命令可以生成RDB文件。
在执行 SAVE 命令的过程中,会阻塞Redis服务器进程,此时Redis服务器将不能继续执行其他命令请求,直到 RDB 文件创建完毕为止。
执行 SAVE 命令:
127.0.0.1:6379>SAVE
OK
返回 OK 表示 RDB 文件保存成功。
在执行 BGSAVE 命令的过程中,BGSAVE 命令会派生出一个子进程,交由子进程将内存中的数据保存到硬盘中,创建 RDB 文件;而 BGSAVE 命令的父进程可以继续处理来自客户端的命令请求。
执行 BGSAVE 命令:
127.0.0.1:6379>BGSAVE
Background saving started
返回 Background saving started 信息,但我们并不能确定 BGSAVE 命令是否已经成功执行,此时可以使用 LASTSAVE 命令来查看相关信息。
执行 LASTSAVE 命令:
127.0.0.1:6379> LASTSAVE
(integer) 1531998138
返回一个 UNIX 格式的时间戳,表示 近一次 Redis 成功将数据保存到硬盘中的时间。
其实,真正创建 RDB 文件的不是 SAVE 或 BGSAVE 命令,而是 Redis 中的 rdb.c/rdbSave 函数。在执行 SAVE 或 BGSAVE 命令后,会以不同的方式调用执行这个函数,进而完成
RDB 文件的创建。
RDB 文件的创建可用于在启动 Redis 服务器的时候恢复数据库的状态,起到备份数据库的作用。RDB 文件只有在启动服务器的时候才会被加载。当启动服务器时,它会检查 RDB 文件是否存在,如果存在,就会自动加载 RDB 文件。除此之外,RDB 文件不会被加载,因为 Redis 中没有提供用于加载 RDB 文件的命令。
在启动 Redis 时,部分日志信息如图 11.6 所示。
[图片上传失败...(image-8df838-1627917286535)]
其中的信息 DB loaded from disk: 0.000 seconds 就是服务器在加载完 RDB 文件之后打印的,这里的时间为 0.000,是因为 RDB 文件过小,加载几乎不耗费时间。
在启动 Redis 服务器的时候,到底是先加载 AOF 文件,还是先加载 RDB 文件呢?
在通常情况下,AOF 文件的更新频率比 RDB 文件的更新频率高得多,服务器每执行一条写命令,就会更新一次 AOF 文件。
其实,在启动 Redis 服务器的时候,会执行一个加载程序,这个加载程序会根据 Redis 配置文件中是否开启了 AOF 持久化,来判断是加载 AOF 文件还是加载 RDB 文件。
● 如果在 Redis 配置文件中开启了 AOF 持久化(appendonly yes),那么在启动服务器的时候会优先加载 AOF 文件来还原数据库状态。
● 如果在 Redis 配置文件中关闭了 AOF 持久化(appendonly no),那么在启动服务器的时候会优先加载 RDB 文件来还原数据库状态。
加载 RDB 文件的实际工作由 rdb.c/rdbLoad 函数完成。
服务器启动加载 AOF 文件或 RDB 文件的过程如图 11.7 所示。
[图片上传失败...(image-f4dd3-1627917286540)]
图 11.7 服务器启动加载 AOF 文件或 RDB 文件的过程
10.3.4 创建与加载RDB文件时服务器的状态
在创建文件时,服务器的状态具体如下:
当执行 SAVE 命令的时候,将会阻塞 Redis 服务器,客户端发送过来的命令请求将会被拒绝执行。只有当 SAVE 命令执行结束之后,服务器才能再次接收并执行来自客户端的命令请求。
当执行 BGSAVE 命令的时候,将会启动一个子进程来创建并保存 RDB 文件,在子进程创建 RDB 文件的过程中,父进程仍然可以处理来自客户端的命令请求。
如果在服务器执行 BGSAVE 命令的过程中,客户端向服务器发送过来的命令是 SAVE、
BGSAVE 或 BGREWRITEAOF,就会有不同的执行策略,具体说明如下:
● 在执行 BGSAVE 命令的过程中,如果客户端发送过来的命令是 SAVE,那么该命令会被服务器拒绝执行。因为在执行 SAVE 命令的时候,服务器会被阻塞,这个时候子进程也在执行,就会造成父进程和子进程同时调用 rdbSave 函数,产生竞争条件。
为了避免这种情况的发生,服务器就会拒绝执行 SAVE 命令。
● 在执行 BGSAVE 命令的过程中,如果客户端发送过来的命令是 BGSAVE,那么服务器会拒绝执行 BGSAVE 命令。因为服务器如果同时执行两个 BGSAVE 命令,则也会产生竞争条件。
● 在执行 BGSAVE 命令的过程中,如果客户端发送过来的命令是 BGREWRITEAOF,那么这个命令会推迟到 BGSAVE 命令执行完成之后才会被执行。相反地,如果在执行 BGREWRITEAOF 命令的过程中,客户端发送过来的命令是 BGSAVE,那么服务器会拒绝执行 BGSAVE 命令。BGSAVE 和 BGREWRITEAOF 命令都是采用子进程来完成任务的,所以这两个命令不能同时执行。
服务器在加载 RDB 文件时,会一直处于阻塞状态,直到 RDB 文件加载完毕,才会变为运行状态。
使用 INFO PERSISTENCE 命令来查看 RDB 持久化的相关状态,操作如下:
127.0.0.1:6379> INFO PERSISTENCE

Persistence loading:0 rdb_changes_since_last_save:12 rdb_bgsave_in_progress:0 rdb_last_save_time:1541692112 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:-1 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:0

10.3.5 RDB 持久化的配置
RDB 持久化的配置具体如下。
● save m n:表示在时间 m 内被修改的键的个数大于 n 时,会触发 BGSAVE 命令的执行。它是 BGSAVE 命令自动触发的条件;如果没有设置该配置,则表示自动的 RDB 持久化被关闭。
● stop-writes-on-bgsave-error yes:当执行 BGSAVE 命令出现错误时,Redis 是否终止执行写命令。参数的值默认被设置为 yes,表示当硬盘出现问题时,服务器可以及时发现,及时避免大量数据丢失;当设置为 no 时,就算执行 BGSAVE 命令发生错误,服务器也会继续执行写命令;当对 Redis 服务器的系统设置了监控时,建议将该参数值设置为 no。
● rdbcompression yes:是否开启 RDB 压缩文件,默认为 yes 表示开启,不开启则设置为 no。
● rdbchecksum yes:是否开启 RDB 文件的校验,在服务器进行 RDB 文件的写入与读取时会用到它。默认设置为 yes。如果将它设置为 no,则在服务器对 RDB 文件进行写入与读取时,可以提升性能,但是无法确定 RDB 文件是否已经被损坏。
● dbfilename dump.rdb:用于设置 RDB 文件名,可以通过命令来修改它,命令格式如下:
CONFIG SET dbfilename RDB文件名
● dir ./:RDB 文件和 AOF 文件所在目录,默认为 Redis 的根目录。
10.3.6 RDB 持久化的优劣
RDB 持久化具有以下优点:
● RDB 文件是一个经过压缩的二进制文件,文件紧凑,体积较小,非常适用于进行数据库数据备份。
● RDB 持久化适用于灾难恢复,而且恢复数据时的速度要快于 AOF 持久化。
● Redis 采用 RDB 持久化可以很大程度地提升性能。父进程在保存 RDB 文件时会启动一个子进程,将所有与保存相关的功能交由子进程处理,而父进程可以继续处理其他相关的操作。
RDB 持久化具有以下缺点:
● 在服务器出现故障时,如果没有触发 RDB 快照执行,那么它可能会丢失大量数据。
RDB 快照的持久化方式决定了必然做不到实时持久化,会存在大量数据丢失。
● 当数据量非常庞大时,在保存 RDB 文件的时候,服务器会启动一个子进程来完成相关的保存操作。这项操作比较耗时,将会占用太多 CPU 时间,从而影响服务器的性能。
● RDB 文件存在兼容性问题,老版本的 Redis 不支持新版本的 RDB 文件。
10.4 AOF持久化与RDB持久化抉择
在实际的应用场景中,由于存在各种风险因素,不知道服务器在什么时候会出现故障,也不知道在什么时候可能会断电,又或者有其他一些意想不到的事情发生,这些事情的发生可能会导致Redis数据库丢失大量数据造成经济损失。为了避免这些情况的发生,建议同时使用AOF持久化和RDB持久化以便最大限度地保证数据的持久化与安全性。
可以只使用RDB持久化,因为RDB持久化能够定时生成RDB快照,便于进行数据库数据备份,同时也能提高服务器的性能,而且RDB恢复数据的速度快于AOF恢复数据的速度,但必须承受如果服务器出现故障会丢失部分数据的风险。
很多用户只使用AOF持久化,但不推荐使用这种方式,因为AOF持久化产生的AOF 文件体积较大,在恢复数据时会比较慢,会严重影响服务器的性能,在生成AOF文件时还可能会出现AOF程序Bug。
如果现在使用的是RDB持久化,想切换到AOF持久化,可以执行以下几步操作:
(1)备份RDB文件(dump.rdb)并将备份文件放到一个安全的地方
(2)在不重启服务器的情况下,执行以下命令:
CONFIG SET appendonly yes #开启AOF持久化,服务器开始初始化AOF文件,此时Redis服务器会发生阻塞,直到AOF文件创建完毕为止,之后服务器才会继续处理来自客户端的命令请求,将写命令追加到AOF文件中。同时需要手动修改Redis的配置文件redis.conf中的appendonly参数值为yes,否则在下一次重启的时候,并不会切换使用AOF持久化
CONFIG SET save " " #关闭RDB持久化。也可以不执行该命令,同时使用RDB持久化和AOF持久化
(3)执行上述命令后,需要检查数据库中的键数量有没有改变,同时确保写命令会被追加到AOF文件中

你可能感兴趣的:(第十章Redis持久化)