Redis是内存数据库,利用以下两种技术进行持久化。
AOF(append only file)
通过保存写操作到日志的持久化方式。
一、为什么redis先执行写操作后,再将命令记录到AOF日志中?
- 避免额外检查开销
- 不会阻塞当前写操作命令的执行
- 但是会存在两个风险:丢失风险(写操作后发生宕机,没来得及记录日志);记录日志不会阻塞当前写操作,但是可能阻塞写一个命令。
二、Redis写入AOF的过程
图片来自小林coding
- Redis 执行完写操作命令后,会将命令追加到
server.aof_buf
缓冲区; - 然后通过 write() 系统调用,将 aof_buf 缓冲区的数据写入到 AOF 文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓冲区 page cache,等待内核将数据写入硬盘
- 具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。
三、三种写回策略
问题一中提到的两个风险,均取决于Redis写入AOF的过程中的第三步,即内核将数据写入到硬盘中,因此Redis提供了三种写回硬盘的策略。
- Always,每次写操作完成后,同步将AOF日志写回硬盘
- Everysec,写操作完成后,将命令写到AOF文件的内核缓冲区,每隔一秒将缓冲区内容写入到硬盘
- No,写操作完成后,将命令写入到AOF文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写入到硬盘。
图片来自小林coding
四、AOF重写机制
随着AOF日志越来越大,会带来性能的影响,比如重启Redis,读取AOF文件会变慢。当AOF文件过大时,会触发重写机制,压缩AOF文件。
重写机制的过程是,读取当前数据库中的所有键值对,将每个键值对用一条命令记录到新的AOF文件中,等到全部记录完成,利用新的AOF替换现在的AOF文件。
五、AOF后台重写
重写过程是很耗时的,采用后台子进程完成重写。
好处:
- 避免阻塞主进程,在重写过程中,主进程还可以继续处理命令
- 子进程带有主进程的数据副本,父子进程共享内存数据,并且只能是只读,当父子进程任意一方修改共享内存,就会发生写时复制,于是父子进程有了独立的数据副本,不用通过加锁保证数据安全。
- 不采用多线程,是因为多线程之间共享数据,需要通过加锁保证数据安全,会降低性能。
六、写时复制
创建子进程时,操作系统会把主进程的「页表」复制一份给子进程,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存。当发生写操作的时候,操作系统才会去复制物理内存。这样是为了防止创建子进程时长时间阻塞主进程。
存在问题:
- 重写AOF日志过程中,如果主进程修改了已经存在的key-value,此时子进程和主进程的数据不一致
解决方式:
Redis 设置了一个 AOF 重写缓冲区,在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 「AOF 缓冲区」和 「AOF 重写缓冲区」。当子进程完成 AOF 重写工作,主进程将将 AOF 重写缓冲区中的所有内容追加到新的 AOF 的文件中。
RDB快照
RDB 文件的内容是二进制数据。RDB 快照就是记录某一个瞬间的内存数据,记录的是实际数据。
一、两个命令生成RDB文件
- save,会在主进程生成RDB文件,可能会阻塞主进程
- bgsave,创建一个子进程生成RDB文件。
二、AOF和RDB的区别
- AOF 文件的内容是操作命令;RDB 文件的内容是二进制数据
- RDB 恢复数据的效率会比 AOF 高些
- Redis 的快照是全量快照,也就是说每次执行快照,都是把内存中的「所有数据」都记录到磁盘中