目录
1. 定义
Redis数据全部在内存里,如果突然故障则数据就会全部丢失,因此必须有-种机制来保证数据不会因为故障而丢失,这种机制就是 持久化机制;
2. 持久化方案
2.1 简介
Redis持久化机制有2种:
①快照:快照是一次全量备份,快照是内存数据的二进制序列化形式,在存储上非常紧凑;
②AOF日志:AOF日志是连续的增量备份,AOF日志记录的是内存数据修改的指令记录文本,鉴于AOF日志在长期的运行过程中会变得无比庞大,当数据库重启时需要加载AOF 日志进行指令重放,加载时间会无比漫长,因此需要定期进行AOF重写,给AOF日志进行瘦身;
2.2 快照RDB
鉴于Redis是单线程运行,同时还要通过非阻塞IO + 多路复用方式来对多个客户端套接字提供并发读写、内存数据读写操作,那么,Redis如何用单线程提供上述服务的同时,还要进行磁盘IO操作呢(磁盘读写是非常慢的,会严重拖累CPU等系统资源)?
答:Redis使用操作系统的多进程COW(Copy-On-Write)机制来实现快照持久化;
2.3 COW
Redis持久化时,通过调用glibc函数来fork产生一个子进程,快照持久化完全交给这个子进程来处理,而父进程继续处理客户端请求。
子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段,这是Linux操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来,在进程分离的瞬间,内存的增长几乎没有明显变化;
当子进程做数据持久化,不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到碰盘中,而父进程则继续服务于客户端的请求,按照客户端的指令对内存数据结构进行不间断的读写;
这里就涉及到了COW机制:由于子进程与父进程共享数据段,而数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会把这个被共享的页面复制一份然后分离出去,然后继续对这个复制的页面进行修改,此刻子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据,而父进程则继续执行着客户端的命令,这就是COW机制;
至于占用的内存吗,虽然随着父进程修改操作的持续进行,越来越多的共享页面被分离出来,内存就会持续增长,但一般不会超过原有数据内存的2倍大小,主要是Redis实例里冷数据占的比例往往是比较高的,所以很少会出现所有的页面都被分离的情况;
子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,这就是快照的由来,子进程可以非常安心地遍历数据,进行序列化写磁盘了;
2.4 AOF日志
AOF曰志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令记录,Redis会在收到客户端修改指令后,进行参数校验、逻辑处理,如果没问题,就立即将该指令文本存储到 AOF曰志中,即先执行后存盘;
鉴于AOF日志的工作流程,Redis在长期运行的过程中, AOF日志会越来越大 ,当实例宕机重启后,需要重放整个AOF曰志会非常耗时,导致Redis长时间无法对外提供服务,所以需要对AOF日志瘦身;
2.5 AOF日志瘦身
Redis提供了bgrewriteaof指令来对AOF曰志进行瘦身,其原理就是开辟一个子进程对内存进行遍历,转换成一系列Redis操作指令,序列化到一个新的AOF曰志文件中,序列化完毕后再将操作期间发生的增量AOF日志追加到这个新的 AOF日志文件中,追加完毕后就立即替代旧的AOF曰志文件了,瘦身工作就完成了;
2.6 AOF日志瘦身原理
AOF日志是以文件的形式存在的,当程序对AOF日志文件进行写操作时,实际上是将内容写到了内核为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘的;
为了确保任何时刻,都能把内存缓存中的数据刷回磁盘中,Linux 的glibc提供的fsync函数可以将指定文件的内容强制从内核缓存刷到磁盘,只要Redis进程实时调用fsync函数就可以保证AOF日志不丢失;
但是fsync是一个磁盘IO操作,如果Redis执行一条指令就要 fsync一次,那么Redis性能下降很快,所以在生产环境的服务器中, Redis通常是每隔ls左右执行一次fsync操作,在保持高性能的同时,尽可能使数据少丢失;
Redis同样也提供了另外2种策略:永不调用fsync让操作系统来决定何时同步碰盘、通过指令显示调用fsync一次;
3.总结
持久化方式 | 生成文件 | 有关指令 | 配置 | 修复工具 |
RDB快照 | dump.rdb | SAVE BGSAVE LASTSAVE | Redis.conf下面的#SNAPSHOTTING#模块 | |
AOF | appendonly.aof | BGREWRITEAOF INFO | appendonly yes#来开启AOF | redis-check-aof |
4.RDB在Redis.conf中的配置
# 指定RDB触发策略: # save <seconds> <changes> # 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件 # Note: 可以把所有“save”行注释掉,这样就取消同步操作了 save 900 1 save 300 10 save 60 10000
# 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大 rdbcompression yes
# 指定本地数据库文件名,默认值为dump.rdb dbfilename dump.rdb
# 指定本地数据库存放目录,文件名由上一个dbfilename配置项指定 # 注意,这里只能指定一个目录,不能指定文件名 dir ./ |
5.AOF在Redis.conf中的配置
# 开启AOF功能: # 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。 # 因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no,即禁用AOF appendonly no
# 指定更新日志文件名,默认为appendonly.aof appendfilename appendonly.aof
# 指定AOF触发策略 # no:服务器不主动将命令写入硬盘,由操作系统决定何时将缓冲区里面的命令写入到硬盘里面,丢失命令数量不确定# always:服务器每写入一条命令,就将缓冲区里面的命令写入到硬盘里面,服务器就算意外停机,也不会丢失任何已经成功执行的命令数据 # everysec:服务器每一秒将缓冲区里面的命令写入到硬盘里面,这种模式下,服务器即使遭遇意外停机,最多只丢失1秒的数据 appendfsync everysec
# 在执行bgrewriteaof时,子进程重写AOF日志时,子进程与主进程同时操作AOF文件时,子进程与主进程抢夺文件锁的情况,然后主进程可以被阻塞,处于等待状态,此刻,如果no-appendfsync-on-rewrite为no,缺点是主进程等待获取文件锁,优点是bgrewriteaof执行确保数据最安全;如果no-appendfsync-on-rewrite为yes,bgrewriteaof先把数据写入了缓冲区,优点是不与主进程抢夺文件锁,缺点在于如果这个时候redis挂掉,就会丢失数据。 因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受数据丢失,则设置为no。 no-appendfsync-on-rewrite no
# 自动触发BGREWRITEAOF命令的策略,可以避免内存数据少,而AOF文件体积大的情况: # 下面2个参数的意思是:当AOF的体积大于64M,并且比上一次重写之后的体积大了至少一倍(100%)的时候,Redis将执行BGREWRITEAOF命令。 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb |