redis为何需要持久化
redis是一个键值对的内存数据库,它将数据及状态保存在内存中,保证了对数据的最快访问速度,但是存在一个问题:当redis服务重启之后,所有数据及状态将全部丢失,所以:为了保证数据在奔溃重启后不丢失,redis需要实现自己的持久化机制,将内存数据存储到磁盘中
redis的持久化机制
redis为数据持久化提供了两种方案:RDB,AOF
redis的RDB持久化机制
RDB持久化功能是将redis内存中的键值对数据保存到一个经过压缩的二进制RDB文件中,通过这个RDB文件可以逆向还原redis服务器生成RDB文件时的数据库状态
RDB文件创建
有两个redis命令可以用于生成RDB文件,一个是SAVE,一个是BGSAVE
save命令执行机制
SAVE命令会阻塞redis服务器进程,执行rdbSave命令直到RDB文件创建完毕为止,在服务器阻塞期间,服务器不能接受任何命令请求
SAVE执行时服务器状态
redis服务器被阻塞,任何请求在阻塞期间都被拒绝
BGSAVE执行机制
服务器进程会fork出一个字进程,由子进程负责执行rdbSave命令创建RDB文件,服务器进程继续处理命令请求
BGSAVE执行时服务器状态
BGSAVE不阻塞redis服务器,redis服务器可以继续处理客户端的命令
BGSAVE期间执行SAVE,BGSAVE,BGREWRITEAOF命令的情况
1,BGSAVE期间禁止执行SAVE命令,避免服务进程与子进程同时执行rdbSave而产生竞争
2,BGSAVE期间也禁止执行BGSAVE命令,避免两个子进程同时调用rdbSave而产生竞争
3,BGRWITEAOF命令与BGSAVE不能同时执行。如果BGSAVE命令正在执行,那么客户端发送BGRWITEAOF的命令会被延迟到BGSAVE命令执行完成之后才执行;如果BGREWRITEAOF命令正在执行,客户端发送的BGSAVE命令会被服务器拒绝
RDB文件载入
RDB文件的载入工作是在服务器启动时自动执行的,所以redis并没有专门的载入RDB文件的命令,只要redis检测到RDB文件就会自动载入
问题:当同时开启AOF持久化功能,rdis如何执行文件加载
如果服务器开启了AOF功能,由于AOF的更新频率要高于RDB的,所以redis服务器会优先使用AOF文件来执行refis内存数据库还原的
RDB自动间隔性保存
自动保存配置
由于redis服务器的BGSAVE命令可以在不阻塞服务器进程的情况下执行,所以redis允许用户可以通过设置服务器的配置save选项,让服务器每隔一段时间自动执行一次BGSAVE命令;用户可以通过为save选项设置多个保存条件,只要其中一个条件满足,服务器就执行BGSAVE命令;具体在redis.conf配置文件中配置
如图可知,命令模式:save
检查是否满足自动保存
redis服务器周期操作函数serverCron默认每100ms执行一次扫描,检查save选线设置的保存条件是否满足,如果满足就执行BGSAVE命令
RDB方式总结
SAVE命令写RDB文件会阻塞redis主进程
BGSAVE命令通过redis主进程fork子进程来写RDB文件,redis利用fork的copy-on-write机制拷贝父进程当前所有状态包括内存,然后执行RDB文件写入,写入过程里,父进程依旧接受客户端写命令,所以RDB文件是非实时的,丢失了执行BGSAVE命令期间父进程所执行的写命令数据
RDB自动间隔性保存是基于BGSAVE实现
由于RDB方式的时间粒度粗,而且无法做到实时保存,所以不适合做容灾,适合做数据冷备
redis的AOF持久化机制
AOF方式是通过保存redis服务器所执行的写命令来记录数据库状态,被写入AOF文件的所有命令都是以redis的命令请求协议保存的,因为redis的命令请求是纯文本格式
AOF持久化功能实现
AOF持久化功能实现可以分为命令追加,文件写入,文件同步三步
redis的服务器进程就是一个事件循环(event-loop);循环中文件事件负责接收客户点命令请求并向客户端发送命令回复;时间事件负责执行serverCron函数这种需要定时运行的函数
命令追加
服务器在执行完写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区末尾
AOF文件写入和同步
事件循环(event-loop)结束之前,调用flushAppendOnlyFile函数,根据服务器配置属性appendfsync的定义,选择执行将aof_buf缓冲区写入保存到AOF文件里
appendfsync=always:服务器在每个事件循环结束之前都会将aof_buf内容写入AOF文件中,并且同步AOF文件落盘;该配置是安全性最高的,即使故障停机也只会丢失一个事件循环(event-loop)中所产生的命令;但是效率是三个选项中最差的,因为频繁的IO以及文件同步
appendfsync=everysec:服务器将每个事件循环(event-loop)都会将aof_buf缓冲区所有内容写入AOF文件,并且每隔一秒就在子线程中对AOF文件做文件同步落盘,属于最实用的配置,保障了效率,且安全性方面丢失一秒数据
appendfsync=no:服务器将每个事件循环(event-loop)都会将aof_buf缓冲区所有内容写入AOF文件,但是不执行主动文件同步落盘,至于何时同步文件由操作系统机制决定,效率方面最快,安全性最差,丢失上次同步AOF文件之后的所有命令
AOF文件载入和数据还原
AOF文件包含了构建数据库状态的所有写命令,所以服务器只要读取并重新执行一遍AOF文件中保存的写命令,就可以还原服务器关闭之前的数据库状态
读取AOF并还原数据库状态步骤
1,创建一个不带网络连接的伪客户端(fake-client)
2,从AOF中分析并读取一条写命令
3,使用伪客户端执行写命令
4,重复执行步骤2,3直到AOF文件被完整执行
AOF重写
AOF通过保存被执行的写命令来记录数据库状态,所以随着时间流逝,AOF文件内容会越来越多,文件体积会越来越大,如果不加约束控制,体积过大的AOF文件可能会对redis服务器造成影响,而且使用AOF恢复数据校验状态的操作时间会更久
为了解决AOF文件体积膨胀问题,redis提供AOF文件的重写功能,通过AOF重写功能可以创建一个新的AOF文件来替代现有AOF文件,新旧两个AOF文件保存的数据库状态相同,但是新AOF文件不会包含任何浪费空间的冗余命令,所以新AOF文件通常会比AOF文件体积小很多
AOF重写实现
AOF重写实现原理:redis首先从数据库读取现有key的value,然后用一条命令去记录当前键值对,代替之前记录的这个键值对的所有命令;所以AOF重写生成的新AOF文件只包含还原当前数据库状态所必须的命令所以新AOF文件不会浪费任何磁盘空间
AOF后台重写(BGREWRITEAOF)
aof_rewrite函数可以很好的完成创建一个新的AOF文件的任务,但是这个函数会执行大量的写入操作,所以调用这个函数的线程会被长时间阻塞,所以当redis服务器直接调用aof_rewrite函数重写AOF文件期间,服务器将无法处理客户端任何命令请求;作为一种辅助手段,redis肯定不希望AOF重写造成服务器不可用,所以redis使用开启子进程的方式重写AOF
后台重写AOF好处:
1,子进程进行AOF重写期间,服务器进程可以继续处理命令
2,子进程带有服务进程的数据副本,使用子进程而非子线程的原因:避免使用锁的情况下保证了数据安全
后台重写的问题:
子进程重写AOF时,主进程还在执行客户端写命令,导致重写之后的AOF文件与redis数据库状态不一致
后台重写问题解决:
redis设置了AOF重写缓冲区,这个缓冲区在子进程开始AOF重写时开始使用,当主进程执行了客户端的写命令,会将这条被执行的写命令同时发送到AOF缓冲区以及AOF重写缓冲区;当AOF重写完成,子进程向主进程发送一个信号,主进程收到信号之后,会将AOF重写缓冲区内所有内容写入新的AOF文件,保证了新AOF与当前redis数据库状态一致,然后将新AOF文件改名,原子覆盖原AOF文件,完成新旧AOF文件替换
AOF持久化方式总结
AOF通过保存修改数据库状态的写命令来记录服务器的数据库状态
AOF机制是先将写命令写入aof_buf,然后根据appendfsync属性定义来执行不同文件写入同步策略
appendfsync属性本设置为always时安全度最高只会丢失一个event-loop中的写命令,被设置为no时安全度最低会丢失自上次之后的所有写命令,而everysec最实用,丢失一秒,性能效率稳健
AOF重写是为了避免AOF文件记录冗余的键值对命令导致文件体积膨胀影响服务器性能;它的机制是生成新不包含冗余键值对命令的AOF文件来替代旧AOF文件;它的实现方式是遍历AOF文件读取数据库中键值对并记录的方式
AOF后台重写是开启新的子进程,开辟AOF重写缓冲区的方式实现,实现方式子进程执行AOF重写,主进程将AOF重写期间的写命令会同步到aof缓冲区以及aof重写缓冲区,当子进程AOF重写完成,通知主进程将AOF重写缓冲区的数据写入到新AOF文件中,而后原子性的重命名并替换旧AOF,后台AOF重写完成