Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以Redis提供了持久化功能!
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建 ( fork )一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
在生产环境我们会将这个文件进行备份
rdb保存的文件是dump.rdb 在配置文件中配置
Redis 的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中,这就类似于给 100 个人拍合影,把每一个人都拍进照片里。这样做的好处是,一次性记录了所有数据,一个都不少。
这样就会产生一个问题,当Redis中的数据越大时,快照文件写入磁盘的时间也就越长。那么Redis在写入磁盘的时候会阻塞主线程吗?这就关系到是否会影响Redis的性能了。
Redis提供了两个命令来生产全量的RDB文件,一个是 save ,另一个是 bgsave。
save:在主线程中执行,会导致阻塞;
bgsave:创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。
不用想也知道,我们应该使用哪种方式来生成RDB文件了。
我们知道了,RDB文件生成的两种命令,那么接下来说下他的具体实现逻辑:
如上图所示,快照开始时,主线程会fork出一个用于快照操作的子线程,并且复制一份数据对应的映射页表给子线程。子线程可以通过这个页表访问主线程的原始数据,然后将数据生成快照文件,存储到磁盘中。我们知道存储磁盘的时间是比较长的,当这个时候有请求进行想写数据怎么办呢?
这个时候就要用到 写时复制,即当请求需要对键值C进行操作时,主线程会把新数据或修改后的数据写到一个新的物理内存地址上(键值对C’),并修改主线程自己的页表映射。所以,子进程读到的类似于原始数据的一个副本,而主线程也可以正常进行修改。
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
如果一直使用全量同步,一方面时间的推移,磁盘存储的快照文件会越来越多。另一方面如果频繁的进行全量同步,则需要主线线程频繁的fork出bgsvae线程,这样对Redis的性能是会产生影响的,并且也需要持续的对磁盘进行写操作。
这个时候,我们可以采用另一只同步方式:增量快照。所谓增量快照,就是指,做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。在第一次做完全量快照后,T1 和 T2 时刻如果再做快照,我们只需要将被修改的数据写入快照文件就行。但是,这么做的前提是,我们需要记住哪些数据被修改了。你可不要小瞧这个“记住”功能,它需要我们使用额外的元数据信息去记录哪些数据被修改了,这会带来额外的空间开销问题。如下图所示:
如果我们对每一个键值对的修改,都做个记录,那么,如果有 1 万个被修改的键值对,我们就需要有 1 万条额外的记录。而且,有的时候,键值对非常小,比如只有 32 字节,而记录它被修改的元数据信息,可能就需要 8 字节,这样的画,为了“记住”修改,引入的额外空间开销比较大。这对于内存资源宝贵的 Redis 来说,有些得不偿失。
所以说,全量快照和增量快照都有各自的优点和缺点,至于实际应用时,则要根据具体情况进行权衡。
触发机制
save的规则满足的条件下,会自动触发rdb规则
执行flushall命令,也会触发rdb规则
退出redis,也会产生rdb文件
备份就自动生成一个dump.rdb
如何恢复rdb文件
只需要将rdb文件放到redis启动目录就可以,redis启动会自动检查dump.rdb恢复其中的数据
查看需要存放的位置
config get dir
优点:
缺点:
Redis是通过一条条命令行将数据存到内存中的,当Redis服务器意外宕机时,内存中的数据就会丢失,这个时候就需要使用AOF机制来恢复Redis的数据了。AOF机制就是将Redis执行的命令行记录成日志文件,然后存入到磁盘中。当出现意外后,将日志文件拿出来,然后再重新执行一遍,来防止数据的丢失。
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
Aof保存的是appendonly.aof文件
默认是不开启的,需要手动进行配置 我们只需要将appendonly改为yes就开启了AOF
如果aof文件有错误,redis是启动会不起来的,需要修复这个文件
redis提供了redis-check-aof --fix
如果aof文件大于64m,fork一个新的进程来将文件进行重写
appendonly no # 默认是不开启aof模式的,默认使用rdb方式持久化的,在大部分情况下,rdb够用了
appendfilename "appendonly.aof" # 持久化的文件名字
# appendfsync always#每次修改都会sync。消耗性能
appendfsync everysec#每秒执行一次 sync,可能会丢失这1s的数据!#appendfsync no
#不执行sync,这个时候操作系统自己同步数据,速度最快!
# rewrite 重写
说到日志,我们可能会想到数据库的写前日志,即在数据写入之前,将这些修改数据的记录存到日志文件中,然而Redis的AOF日志正好相反,他是在Redis数据存入内存后再讲这些操作命令记录到数据库中的。如下图所示:
这样做一方面是因为,这些操作命令成功存入到Redis中后,就表明这些语句是正确的,那存入日志文件的时候就不需要再对这些操作命令进行校验了。既保证了存储到日志文件的命令行时正确的,而且也加快了存储的效率。另一方面,在执行命令行后再记录日志也是为了不阻塞主线程的执行。因为记录日志和存储数据都是采用的主线程。
不过AOF也存在两个问题:1、在存储数据后,记录日志的服务器宕机了。这样就会导致这次存储的数据丢失; 2、记录日志和存储数据都是采用的主线程,那当记录日志时,就不可避免的会导致主线程的阻塞,影响Redis的性能。
为了解决这两个问题,Redis设计了三种写回策略:
Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;
Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;
No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。
其实这3中策略其实都没有完美的解决上面的两个问题,世上本就没有十全十美的事情。他们只是在性能和可靠性上做了平衡和取舍。当你需要高可靠时就使用Always,需要高性能时就使用NO,趋中时就使用Everysec。
当Redis运行越久,记录的日志文件就会越大,那么记录日志的时间就会越长,对主线程的阻塞就会越来越严重。这个时候就需要用到AOF重写机制了。
AOF 重写机制就是在重写时,Redis 根据数据库的现状创建一个新的 AOF 文件,也就是说,读取数据库中的所有键值对,然后对每一个键值对用一条命令记录它的写入。比如说,当读取了键值对“testkey”: “testvalue”之后,重写机制会记录 set testkey testvalue 这条命令。这样,当需要恢复时,可以重新执行该命令,实现“testkey”: “testvalue”的写入。
为什么重写机制可以把日志文件变小呢? 实际上,重写机制具有“多变一”功能。所谓的“多变一”,也就是说,旧日志文件中的多条命令,在重写后的新日志中变成了一条命令。我们知道,AOF 文件是以追加的方式,逐一记录接收到的写命令的。当一个键值对被多条写命令反复修改时,AOF 文件会记录相应的多条命令。但是,在重写的时候,是根据这个键值对当前的最新状态,为它生成对应的写入命令。这样一来,一个键值对在重写日志中只用一条命令就行了,而且,在日志恢复时,只用执行这条命令,就可以直接完成这个键值对的写入了。
如上图所示,当我们对一个列表先后做了 6 次修改操作后,列表的最后状态是[“D”, “C”, “N”],此时,只用 LPUSH u:list “N”, “C”, "D"这一条命令就能实现该数据的恢复,这就节省了五条命令的空间。对于被修改过成百上千次的键值对来说,重写能节省的空间当然就更大了。
优点:
缺点:
扩展︰