你应该弄懂redis的两种持久化方式

205 阅读6分钟

1.前言

redis作为内存数据库,最常见的使用场景就是当缓存用。浏览器访问后端应用,后端应用先访问redis,如果redis中有数据,直接返回;否则就去查询数据库。

如你所知,redis作为内存数据库,数据存放在内存中,如果宕机,会导致数据全部丢失。数据丢失,来自浏览器的所有请求都会去查询数据库,一方面会给数据库带来巨大压力;另一方面由于这些请求需要访问数据库,相比访问redis会导致响应时间变长。

假如redis在宕机恢复后数据不丢失,就可以避免给数据库造成巨大压力和导致响应时间变长问题。要想实现数据恢复,可以使用redis持久化方案aofrdb

2.AOF

2.1 官方说明

1.redis官方认为RDB快照方式可以满足大多数应用,因此默认情况下只会开启RDB快照方式进行持久化。

2.RDB快照方式在服务停电时会造成数分钟写丢失,为了保证数据少丢,可以选择使用AOF持久化

3.AOFRDB可以同时开启,如果AOF功能开启,Redis在启动时会加载AOF文件

2.2 开启AOF功能

appendonly后面的no改成yes即可开启AOF功能

2.3 验证AOF功能

通过客户端执行set k1 v1,在AOF文件中可以看到如下内容

2.3 写入时机

redis为了避免检查写命令开销,选择了先写命令,再记日志

2.3 写入策略

redis记日志是在主线程中完成,为了平衡高可靠性高性能,提供了三种写入策略

写入策略优点缺点
no(操作系统写回)性能最高数据丢的最多
always(同步写回)性能最差可以保证几乎不丢数据
everysec(每秒写回)性能适中最多丢1s数据

redis默认情况下选择每秒写回策略,丢失1s数据一般还是比较能接受;如果选择操作系统写回说明能够接受大量数据丢失,此时可以考虑只使用RDB,不开启AOF

2.4 aof文件过大怎么办

redis开启aof持久化功能后,客户端的写命令会被持续记录在aof日志文件中,时间越长,aof日志文件就会越大,那么文件过大会有什么影响呢?

  • 文件系统对文件大小有限制,无法保存过大文件
  • 如果文件过大,之后再往里面追加日志记录,效率也会变低
  • redis宕机,重启过程中会执行aof文件中的记录,如果文件过大,会导致重启时间过长,服务长时间不可用

为了避免这些影响,aof文件在达到一定大小后就需要对其重写

2.5 重写依据

aof文件大小达到64M并且当前大小相比上一次重写后aof文件的大小增长100%就会触发重写操作

2.4 aof重写过程

aof重写可以选择在主线程中执行,主线程读取redis中的数据以命令的方式记录在重写aof文件中,读取完成后,用重写aof文件替换旧aof文件即可完成重写。方案简单易用,问题同样也很明显,那就是在重写完成之前无法对外提供服务。此时你大概会很好奇为什么redis在提供重写能力的同时还能响应客户端的请求,它是如何实现的呢?答案就是fork(),我们来看看fork()究竟干了什么

aof文件达到重写要求后,redis会通过fork()系统调用创建出一个子进程(用来执行重写操作),子进程会拷贝主进程的虚拟地址空间,这样主进程子进程就可以共享内存中的数据。

子进程重写过程中只需要读取内存中的数据以命令方式追加到重写aof文件中即可,重写过程中产生的新的写命令会被主进程记录在重写aof缓冲中,内存中的数据重写完成后再将重写aof缓冲中的命令追加到重写aof文件中,最后用重写aof文件替换原aof文件就完成了整个重写操作。

为了引出下一个知识点,先带你分析一种场景:重新过程中redis中有这样一条数据k1 =1,客户端执行了incr k1命令后k1就等于2,主进程会在重新aof缓冲中记录incr k1命令,由于k1在内存中的数据等于2,子线程重写追加到文件中的命令应该是set k1 = 2,等重写完内存中的数据后,再追加重写aof缓冲中的incr k1命令,就会导致k1实际的值变成3而不是2,产生数据不一致性问题。

如何避免在重写的过程中产生数据不一致性的问题呢?答案就是:写时复制(copy on write(COW)),主进程有写操作发生时,需要将内存中的数据复制一份再进行修改,而不是在原来的数据上进行修改,主进程虚拟地址指向复制后的物理地址;子进程还是指向原来的物理地址,aof重写操作完成后k1的值是2而不是3,与预期结果一致。

3.RDB

3.1 aof持久化存在的问题

由于aof日志文件中记录的是操作命令,在进行数据恢复时,需要将日志文件中的命令逐条执行,一旦日志文件过大,就会导致redis数据恢复时间变长。那么有没有一种方式既可以实现内存数据持久化,又能快速恢复内存数据的解决方案呢?

rdb(redis database):存放的是内存中的数据而非执行的命令,因此在满足内存数据持久化的同时又达到能快速恢复数据的目的。

3.2 rdb流程

aof日志文件重写一样,执行rdb操作的时候主进程也会fork出一个子进程,用于将内存的数据写入到文件中。此时你可能会有疑问,如果子进程在持久过的过程中,内存中的数据发生变化了怎么办?

事实上,子进程持久化的是内存中的快照数据,那么什么是快照数据呢?

快照数据就是某一时刻内存中的数据,子进程在持久化的过程中,如果有写入操作,会通过cow(copy on wirte)技术来保证快照数据不被修改。

3.3 rdb写入频率

根据配置文件说明,使用者可以自定义rdb写入频率,但是设置多少合适呢?频率太高,会造成磁盘写入压力过大,上次一次写入任务还未执行完成下一次写入任务又开启了,从而进入恶性循环;频率太低,会造成数据丢失过多的问题发生,因此写入频率的设置是一个不太好衡量的操作。

3.4 aof + rdb

既然rdb写入频率不太好去衡量,那么我们可以结合aof来解决rbd周期之间数据丢失的问题。使用rdb + aof结合方式既可以满足内存数据的快速恢复,又可以实现内存数据的持久化。