概述
1、为什么要有Redis的日志呢?
因为一旦服务器宕机,那么内存中的数据将全部丢失。我们能想到的一种解决方案是对于一些数据库中也有的数据,我们从数据库中恢复到缓存。但是这会导致数据库的访问压力过大,并且访问数据库性能不算优秀。
2、说下什么是AOF日志?(主要是回答的思路和步骤,提出问题和解决问题的过程)
AOF日志中记录的是Redis收到的每一条命令。
它是写后日志,就是先执行命令后记录日志,与之对应的是mysql的redo log的写前日志。为什么Redis选择写后日志呢?先说下写后日志的优点:因为只有执行成功的命令才会记录到日志中,避免了通过日志恢复数据时报错;另一点是写日志在命令之后执行,不会阻塞当前命令的执行。再说下写后日志的缺点:如果命令执行完还没记录日志就宕机了,没有持久化日志,那么就不能通过日志恢复数据了;另一点虽然写日志不会阻塞当前命令,但是它会阻塞下一个命令。
那么对应这些缺点redis有给出哪些解决方案呢?首先分析下缺点的两个问题就可以得知,这两个问题都是和AOF写回磁盘相关的,对于这个问题,redis给了我们三种写回策略的选择,appendfsync。然后再介绍下三种写回策略的特征和优缺点,之后根据自己的业务进行取舍选择。
但是,有了写回策略并不是就“高枕无忧”了。因为随着执行的命令越来越多,那么AOF日志文件也会越来越大,便会带来一些性能问题:磁盘占用太多;文件太大,之后往里面追加操作的效率过低;如果发生宕机,需要把AOF记录的日志执行一遍,如果文件太大,整个恢复过程非常缓慢,就会影响到redis的正常使用了。
所以,Redis就AOF文件不断变大的问题提供了一定的控制手段:AOF重写机制。简单来说,AOF重写就是根据数据库的现状创建一个AOF文件,然后读取Redis数据库中的所有键值对,并为之生成一条SET命令记录到新的AOF日志中。这样做能做到压缩的原因是因为旧的AOF日志中存在很多冗余命令,比如对同一个key的set命令,只有最后一个才是最终有效的。我们可以通过配置auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数来控制AOF重写的触发时机。
AOF重写便控制了AOF日志不断变大的问题,但是要把整个数据库的数据写入磁盘仍然非常耗时,这时候我们就需要关注一个问题:AOF重写会阻塞主线程么?
把AOF重写的过程说一下:“一个拷贝,两处日志”。具体看下面的详细过程。
3、什么是RDB日志?
RDB叫做内存快照,是Redis DataBase的缩写,和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,所以,在做数据恢复时,我们可以直接把 RDB 文件读入内存,很快地完成恢复,而AOF需要把日志中的操作一个个执行完才行,所以RDB相比于AOF的优点是恢复数据快!
4、实践中如何选择使用两种日志
从实现方式和优缺点对比两个角度回答。
一、AOF
记录的是每一次的执行命令,为什么要在命令执行之后执行呢?
- 是因为只有执行成功的命令才会记录到日志中,避免通过日志恢复数据时报错。
- 第二点是因为在命令之后执行,不会阻塞当前命令的执行
1、缺点
1、如果执行完操作命令之后redis宕机,没有持久化日志,那么就不能通过日志恢复数据了
2、虽然AOF写后执行操作,不会影响阻塞当前命令,但是会对下一个命令有影响。因为AOF日志也是在主线程
中执行的。
观察发现,这两种都是和AOF回写磁盘有关的。
2、AOF的三种回写磁盘策略
所以,没有一种策略能够同时满足性能高和宕机不丢失数据,看取舍吧。总结一下就是:想要获得高性能,就选择 No 策略;如果想要得到高可靠性保证,就选择 Always 策略;如果允许数据有一点丢失,又希望性能别受太大影响的话,那么就选择 Everysec 策略。
3、AOF文件太大的缺点
这里的“性能问题”,主要在于以下三个方面:一是,文件系统本身对文件大小有限制,无法保存过大的文件;二是,如果文件太大,之后再往里面追加命令记录的话,效率也会变低;三是,如果发生宕机,AOF 中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程就会非常缓慢,这就会影响到 Redis 的正常使用。
4、AOF文件缩小策略-AOF重写机制
有两个配置项在控制AOF重写的触发时机:
- auto-aof-rewrite-min-size: 表示运行AOF重写时文件的最小大小,默认为64MB
- auto-aof-rewrite-percentage: 这个值的计算方法是:当前AOF文件大小和上一次重写后AOF文件大小的差值,再除以上一次重写后AOF文件大小。也就是当前AOF文件比上一次重写后AOF文件的增量大小,和上一次重写后AOF文件大小的比值。
5、AOF重写会阻塞主线程么
和 AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。
我把重写的过程总结为“一个拷贝,两处日志”。
“一个拷贝”就是指,每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程。此时,fork 会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。
“两处日志”又是什么呢?
因为主线程未阻塞,仍然可以处理新来的操作。此时,如果有写操作,第一处日志就是指正在使用的 AOF 日志,Redis 会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个 AOF 日志的操作仍然是齐全的,可以用于恢复。
而第二处日志,就是指新的 AOF 重写日志。这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的 AOF 文件,以保证数据库最新状态的记录。此时,我们就可以用新的 AOF 文件替代旧文件了。
总结来说,每次 AOF 重写时,Redis 会先执行一个内存拷贝,用于重写;然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为 Redis 采用额外的线程进行数据重写,所以,这个过程并不会阻塞主线程。
二、RDB
简单说就是内存快照,把内存在某一时刻的数据的状态记录下来,类似于照片。
简单说下过程:当满足RDB触发的条件时,Redis可选择通过使用主线程或者fork出一个子线程的方式来把内存中的Redis数据复制到磁盘的过程。如果不想主线程阻塞,可使用子线程。如果使用RDB,频率的设置需要合理分析,不能频率太高,因为会带来性能问题:一方面是频繁全量数据导入磁盘,会给磁盘带来较大压力;另一方面,虽然子线程不会阻塞主线程,但是主线程fork出子线程的过程会阻塞,尤其是内存数据越大fork时间越长。如果在快照期间有数据的修改,那么会通过操作系统的写时复制技术,在执行快照的同时,处理写操作。
配置RDB持久化(redis.conf)
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发bgsave命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发bgsave命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发bgsave命令创建快照。
Redis 提供了两个命令来生成 RDB 快照文件:
- save : 主线程执行,会阻塞主线程;
- bgsave : 子线程执行,不会阻塞主线程,默认选项。
三、AOF和RDB比较
四、方案选择
最后,关于 AOF 和 RDB 的选择问题,我想再给你提三点建议:
- 数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择;
- 如果允许分钟级别的数据丢失,可以只使用 RDB;
- 如果只用 AOF,优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡
RDB
AOF
混合持久化
因为AOF可以实现数据不丢失,但是恢复数据需要执行每一条命令,速度慢;而RDB快照恢复速度快,但是两次快照之间的频率不好把握,易造成数据丢失。所以redis4.0中提供了混合使用 AOF 日志和内存快照的方法。4.0版本混合持久化默认关闭,5.0版本混合持久化默认开启。
在redis.conf配置中需要同时开启下面这两个。