有了RDB文件之后,如果服务器关机了,或者需要新增一个服务器,重新启动数据库服务器之后,就可以通过载入RDB文件恢复之前备份的数据。
但是bgsave会耗费较长时间,不够实时,会导致在停机的时候丢失大量数据。
RDB文件保存的是数据库的键值对数据,AOF保存的是数据库执行的写命令。
AOF的实现流程有三步:
append->write->fsync
append追加命令到AOF缓冲区,write将缓冲区的内容写入到程序缓冲区,fsync将程序缓冲区的内容写入到文件。
当AOF持久化功能处于开启状态时,服务器每执行完一个命令,就会将命令以协议格式追加写入到redisServer结构体的aof_buf缓冲区,具体的协议这里不展开阐述。
AOF的持久化发生时期有个配置选项:appendfsync。该选项有三个值:
always:所有内容写入并同步到aof文件
everysec:将aof_buf缓冲区的内容写入到AOF文件,如果距离上次同步AOF文件的
no:将aof_buf缓冲区中的所有内容写入到AOF文件,但并不对AOF文件进行同步,由操作系统决定何时进行同步,一般是默认情况下的30s。
AOF持久化模式每个写命令都会追加到AOF文件,随着服务器不断运行,AOF文件会越来越大,为了避免AOF产生的文件太大,服务器会对AOF文件进行重写,将操作相同key的相同命令合并,从而减少文件的大小。
举个例子,要保存一个员工的名字、性别等信息:
> hset employee_12345 name "hoohack" > hset employee_12345 good_at "php" > hset employee_12345 gender "male"只是录入这个哈希键的状态,AOF文件就需要保存三条命令,如果还有其他,比如删除,或者更新值的操作,那命令将会更多,文件会更大,有了重写后,就可以适当地减少文件的大小。
AOF重写的实现原理是先服务器中的数据库,然后遍历数据库,找出每个数据库中的所有键对象,获取键值对的键和值,根据键的类型对键值对进行重写。比如上面的例子,可以合并为下面的一条命令:
> hset employee_12345 name "hoohack" good_at "php" gender "male"。AOF的重写会执行大量的写入操作,Redis是单线程的,所以如果有服务器直接调用重写,服务器就不能处理其他命令了,因此Redis服务器新起了单独一个进程来执行AOF重写。
Redis执行重写的流程:
在子进程执行AOF重写时,服务端接收到客户端的命令之后,先执行客户端发来的命令,然后将执行后的写命令追加到AOF缓冲区中,同时将执行后的写命令追加到AOF重写缓冲区中。
等到子进程完成了重写工作后,会发一个完成的信号给服务器,服务器就将AOF重写缓冲区中的所有内容追加到AOF文件中,然后原子性地覆盖现有的AOF文件。
RDB持久化方式可以只通过服务器读取数据就能加载备份中的文件到程序中,而AOF方式必须创建一个伪客户端才能执行。
RDB的文件较小,保存了某个时间点之前的数据,适合做灾备和主从同步。
RDB备份耗时较长,如果数据量大,在遇到宕机的情况下,可能会丢失部分数据。另外,RDB是通过配置使达到某种条件的时候才执行,如果在这段时间内宕机,那么这部分数据也会丢失。
AOF方式,在相同数据集的情况下,文件大小会比RDB方式的大。
AOF的持久化方式也是通过配置的不同,默认配置的是每秒同步,最快的模式是同步每一个命令,最坏的方式是等待系统执行fsync将缓冲同步到磁盘文件中,大部分操作系统是30s。通常情况下会配置为每秒同步一次,所以最多会有1s的数据丢失。
怎样的同步方式更好?RDB和AOF方式结合。起一个定时任务,每小时备份一份服务器当前状态的数据,以日期和小时命名,另外起一个定时任务,定时删除无效的备份文件(比如48小时之前)。AOF配置为1s一次。这样一来,最多会丢失1s的数据,同时如果redis发生雪崩,也能迅速恢复为前一天的状态,不至于停止服务。
总结Redis的持久化方案也不是一成不变的,纸上的理论还需要结合实践成果来证明其可行性。
原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。