Redis是应用最广泛的NOSQL数据库,是一种高级 KEY-VALUE数据库,和memcached类似,不过是可以持久化的,redis作为内存数据库是如何持久化的呢?今天我们就来聊聊Redis的持久化原理。
redis提供了两种持久化方式。
- aof(APPEND ON FILE)持久化:原理是将redis的操作以命令的方式写入aof文件中。
- rdb(Redis DataBase)内存快照持久化,就是将redis的内存中的数据全量拷贝一份存储在磁盘中。
我们来详细的分析下这两种持久化方式。
aof持久化
aof持久化是将reids的每次执行成功后的命令记录到aof文件中。
执行成功后记录AOF日志,可以保证每次记录的AOF命令都是正确的,错误就直接返回给客户端。
AOF的文件中是如何记录的呢?
我们以redis的一个成功记录的命令为例:
set test 'hello world'
复制代码
aof日志内容中*3表示 此命令由三部分组成【set,test,hello world】,每部分都是以$+数字开头,后面跟上命令、键或者值。其中数字表示后面跟的命令、键或者值的长度(字节)。例如 “$4 test”表示这部分有4个字节,就是 test。
如图所示
redis后记录aof日志的优点
- 在执行命令的过程中已经将错误命令过滤掉,不会将错误命令记录到aof中。
- AOF日志由于命令是执行成功后写入的,所以并不会阻塞操作。
AOF日志的风险
- 如果命令刚执行成功,还没有来得及写入AOF日志,服务器宕机了,则此条数据就会丢失。
- 虽然AOF避免了对当前命令的阻塞,但是AOF日志罗盘动作依然是在主线程中执行的,如果磁盘满了造成了组赛依然会造成下一个命令的阻塞。
redis提供了三种aof写回策略
- always 同步回写:每条命令执行完成后,回立即将日志写回磁盘。
- everysec 每秒回写:每个命令执行完后回先将日志写入aof文件的内存缓冲区,每隔一秒把缓冲区中的命令写入磁盘
- no 操作系统控制的回写:每个写命令执行完后只是先把日志写入aof文件的内存缓冲区,何时回写磁盘由操作系统决定。
我们来分析下三种回写策略。
always回写回占用主线程资源,每次回写都是一个慢速的罗盘操作,基本上可以避免数据的丢失,会对主线程产生影响,如果你对redis数据的准确性要求非常高,而写入和读取占比不高的话,可以采用这种策略。
每秒回写采用一秒回写一次的策略,避免了同步回写的性能开销,虽然减少了对系统性能的影响,但是如果1秒内产生了大量的写操作,在aof缓冲区积压了很多日志,这时候还没来得及写入aof日志文件就发生了宕机,就会造成数据的丢失。
采用操作系统控制的回写,在写完aof缓冲区后就可以继续执行命令,但是如果系统发生宕机了,也同样会造成数据的丢失。
采用哪种策略要根据自己的业务场景去定。
aof日志本身由哪些性能问题呢?
- aof日志是以文件形式将命令存储到磁盘中,如果修改的频繁,redis的数据库也比较大的时候,就会造成aof文件日志过大。
文件过大会造成以下三个方面的问题:
- 如果redis服务器发生重启的时候,依靠aof文件去恢复数据库就会导致执行的时间过长,会对性能产生影响。
- 文件系统对文件的大小本身就有大小限制。
- 文件过大,再往里面加内容的时候,就会变慢。
如何解决这种问题呢?
redis提供了aof重写机制,aof日志是把所有写命令记录下来,如果对一个键值对做十次修改,则就会记录十次,aof重写机制就是把这条键值对的多次操作压缩成一次操作,这样就大大减少了aof日志文件的大小。
尽管aof重写机制大大缩小了aof文件的大小,如果redis数据库本身的数据就比较庞大的时候aof重写也会占用很多时间。
aof重写会阻塞吗?
aof日志的重写是由后台程序bgrewriteaof子进程完成的,这也是为了避免阻塞主线程,导致数据性能下降。
在重写的时候,redis主进程会fork出一个bgrewriteaof子进程,bgrewriteaof会在不影响主进程的情况下完成回写。
如果在回写的过程中,主进程有命令写入怎么办?
如果有新的写入,主进程会在老的aof缓冲区写入,也会在新的aof文件缓冲区写入,这样就算重写失败,老的aof数据也不会丢失,新的aof文件重写完成后,可以把aof文件缓冲区再次写入aof中,新的aof也不会丢失文件。
RDB日志
RDB持久化方式时以内存快照的方式把文件存储到磁盘中,文件是以.rdb结尾,里面存储的是二进制数据。
两种RDB存储方式
- save:save 是需要占用主线程资源的,会阻塞主线程。
- bgsave:使用的是子进程写,不会占用主线程的资源(fork会阻塞主线程,但是copyonwrite过程是纳秒级别相对较快,aof是毫秒级别)。
redis为了数据的可靠性,每次rdb的时候都是全量快照。
如果redis在执行快照的时候,有数据写入了怎么办?
redis使用了操作系统的写时复制技术(COPY -ON-WRITE COW)
bgsave子进程是由主进程fork生成的,可以共享主进程的内存,如果主进程有写入命令时候,主进程会把写入的那块内存页先复制一份给fork使用,这样就不影响主进程的写操作,可以继续修改原来的数据,避免了对正常业务的影响。