持久化
持久化指的是长时间存储数据。
redis是一个内存数据库,将数据存放在内存里,如果希望redis实现持久,就需要把数据存储到硬盘上。
为了保证数据读写速度快,数据就需要放在内存里,为了持久,数据就要存储在硬盘上,所以redis在内存和硬盘上都存储了数据,硬盘中的数据用于redis重启的时候,恢复内存中的数据。
策略
redis实现持久化主要采用以下两种策略。
RDB
也就是Redis Database,定期将内存中的所有数据存到硬盘中,生成一个快照。
这里的触发方式分为两种,一个是自动触发,另外一个是手动触发,比如说使用save/bgsave命令进行触发,执行save的时候,redis会全力执行快照生成的操作,而redis是单线程的,所以此时其他的任务就会被阻塞。
bgsave :bgsave这个命令执行的时候,不会影响其他客户端请求的处理,这个命令使用了多进程的方式完成的并发编程。

redis服务器可能会同时收到多个客户端发出的bgsave请求。
- 如果当前已经有一个子进程在执行bgsave了,那么就会直接把当前还需要执行的bgsave返回。
- 如果没有子进程在执行bgsave,那么就通过fork这样的系统调用创建出一个子进程出来,由于fork后的子进程会继承父进程中的数据,所以也就相当于将父进程中的数据持久化了。
- 子进程完成持久化过程,父进程继续接收处理客户端的请求。
rdb文件 :当执行rdb文件生成操作的时候,会将生成的快照数据放置在一个临时文件里,当这个快照生成完毕以后,再删除之前的rdb文件,将新生成的临时rdb文件名字修改为dump.rdb,这样,无论什么时候,快照文件都只有一个。
如果我们想要观察到这个过程,可以在执行bgsave前后观察文件的inode:

那我们要怎么找到这个rdb文件呢?
可以使用cd /etc/redis里查看配置文件里rdb文件存放的路径。


缺点 :每次都需要把数据全部拷贝一遍,消耗是比较大的,所以生成的频率不能太频繁(在配置文件里可以修改生成的条件,必须两个都满足才会执行修改),导致很多时候RDB文件和实时数据是有一些偏差的,而且RDB使用特定的二进制格式进行存储,redis演进过程中可能有多个rdb格式版本,兼容性存在问题。

以上的方式只是触发保存的一种方式,在我们修改key以后,立刻重启redis服务器,也会触发自动保存。
优点 :rdb是一个高度紧凑压缩的二进制文件,代表redis在某个时间节点上的数据快照,非常适用于备份和全量复制的场景,并且AOF是使用文本方式来组织数据的,所以RDB加载数据远远快于AOF。
rdb文件出错 :当我们修改了redis的rdb文件,并且使用kill命令强行关闭redis服务器,得到的后果是不可预期的,当我们不确定rdb文件有没有被破坏的时候,可以使用redis提供的redis-check-rdb来检查rdb文件的格式。


AOF
也就是Append Only File,实时将数据存到硬盘中,类似于MySQL的binlog,将用户的所有操作记录到文件中,当开启AOF的时候,RDB就不生效了,重启的时候默认读取AOF。
当我们想要开启AOF模式的时候,可以在配置文件路径下寻找相关配置项appendonly,将no改为yes,再使用service redis-server restart

我们可以发现,在/var/lib/redis路径下出现了一个appendonly.aof文件,文件是以文本形式存储的内容。


效率 :引入AOF并不会影响到redis日常请求的速度,因为AOF并不是让工作线程直接将数据写入硬盘,而是先写入内存中的缓冲区,积累一部分才会写入硬盘中,减少写硬盘的次数。
硬盘随机读写的效率是比顺序读写的效率低的,所以AOF使用顺序读写的方式,每次将新的操作写入原有文件的末尾。
缓冲区刷新策略 :如果把数据写入缓冲区,本质上还是在内存中的,此时如果进程挂了或者主机掉电了,缓冲区里没来得及写入硬盘的数据是会丢失的。
刷新频率越高,对性能影响越大,数据可靠性越高,反之亦然,所以redis中提供了1个配置项叫做appendfsync,always表示每一次操作都要将数据写入硬盘,数据可靠性最高,性能最低,everysec表示命令写入的时候只执行write操作,等到每分钟由同步fsync,no表示在命令写入的时候只执行write操作,等OS控制fsync频率,频率是最低的,性能是最高的,数据可靠性是最低的。

重写机制:AOF文件持续增长,会影响redis的启动时间,因为redis启动的时候需要读取AOF文件,有一些操作是冗余的。所以redis就存在一个重写机制,可以对AOF文件进行整理,合并剔除一些操作,减少AOF文件的大小。
-
重写机制可以分为两种,一种是自动触发,根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个配置项进行触发,auto-aof-rewrite-min-size表示触发重写时AOF的最小文件大小,默认为64MB,auto-aof-rewrite-percentage表示当前AOF占用空间大小相比于上次重写时增加的比例。另外一种是手动触发,使用bgrewriteaof进行重写。

-
AOF重写也是需要创建子进程的,父进程继续去接收请求,子进程负责将内存中的数据读取出来,以AOF文件的格式将数据写入一个新的AOF文件中,父进程也会继续将新的操作记录到旧的AOF文件中,当子进程将AOF文件创建完成以后,会通过信号通知父进程。但是在fork的时候,子进程只会继承fork那一瞬间的数据,在后续子进程处理AOF文件的时候,父进程内存中的数据修改是不会影响子进程的,此时,父进程准备了一个aof_rewrite_buf缓冲区,用于接收fork之后改变的数据,当父进程接收到子进程的通知信号以后,就会将aof_rewrite_buf缓冲区里的数据记录到新的AOF文件里。当前正在执行aof重写的时候,执行bgrewriteaof会直接返回,如果当前正在执行rdb文件的生成的时候执行bgrewriteaof,aof重写操作会等待rdb文件生成完成,再执行aof重写。

混合持久化 :由于aof文件是以文本格式记录的,在redis加载的时候效率会比较低,所以redis使用了混合持久化的方式提高效率,也就是按照aof的方式将每一个请求/操作都记录到文件里,在触发aof重写以后,就会将当前的内存状态以二进制的方式存储到新的aof文件中,后续再进行的操作依旧是使用文本的形式追加到文件末尾。

重写之前:

重写之后:
