AOF
-
只会记录写操作命令,读操作命令不会被记录
-
执行流程
- 客户端发送命令给redis
- redis第一步执行写命令写入内存
- 第二步记录命令到日志中
- 例如:
- set name hyggebest
- AOF日志为:*3 4 name $9 hyggebest
- *3(操作分为三部分)、$4 name(字节数)
-
存储过程
- 第一步执行客户端的命令,进行操作
- 第二步写日志,将命令写入AOF的缓冲区,最后才会写入磁盘。(ps:所有的写磁盘操作都不会直接真正写入磁盘中,因为I/O开销大)
-
写入磁盘(策略)
- 流程:redis进入第二步将客户端命令写入AOF buffer缓冲区中,然后如果系统调用witer()命令将日志拷贝到内核缓冲区page cache中。最后的真正写入磁盘中由内核决定。
- reids 可配置策略选项,在redis.conf中的
appendfsync- Always:接收到客户端的命令之后直接写入,来一个我写一个。执行执行fsync()。
- Everysec:每秒执行一次将缓冲区中的日志写入磁盘中。因为日志先存在AOF buffer中的,在接收客户端命令的时候先将命令写入缓冲区中的。创建一个异步任务去调用fsync().
- No:redis永远不会将AOF buffer中的日志写入磁盘的,而是由操作系统决定什么时候写。永远不会调用fsync().
这三种都有一些缺陷吧。Always会有很大的I/O开销,同步写还会影响主进程的性能,但是可以保证数据的完整性;Everysec很大程度上能减少对主进程的性能影响,但是在数据完整性上会丢一些数据,毕竟1s执行一次也就一位如果发生宕机的情况下会丢失1s的数据;No由操作系统决定什么时候写入磁盘的,在数据完整性上无法保证,但是性能好一些。
-
重写机制
- 为了避免AOF文件过大,会带来性能问题。如果重启redis的时候文件过大要执行的命令很多,过程就会很慢,这就是为什么会有重写机制。
- 来看看场景:
- 用户A注册成功并把昵称注册为
madhatter对应写入redisset name madhatter - 然后A用户把用户名改成
hyggebest对应对应redisset name hyggebest - 如果没有重写机制的情况下就会记录着两条命了,但其实
madhatter对于AOF日志是没有意义的。 - 这个时候在使用重写机制的情况下只会读取最新的key-value,这样就只会记录一条记录。这样占用的空间和资源就少很多了。
- 重写工作完成后,会将新的AOF文件覆盖掉现有的AOF文件,相当于压缩文件,这样文件就小很多。
- 为什么不使用现有的AOF文件而是使用重写后的AOF文件呢?因为如果AOF文件重写失败了,现有的AOF文件就会造成污染,可能无法用于恢复使用。
- 用户A注册成功并把昵称注册为
-
AOF后台重写
- 重写时机:当AOF文件大于64MB的时候,会将AOF文件进行重写,这个时候就需要进行重写。需要将缓存中的所有键值对数据取出,并为每个键值对生成一条命令,然后将其写到新的AOF文件,重写完成后就可以替换掉。
- 重写过程是需要耗时间的,不可能一直阻塞,而是由另外的子进程进行完成。
- 重写过程:
- 主进程通过fork系统调用生成bgrewriteaof子进程
- 操作系统将主进程的也表复制一份给子进程,共享一片物理内存,节省空间。也表对应的也表项的属性会标记该物理内存权限为只读
- 如果在重写的过程中,子进程或主进程发生内存操作(写操作)的时候,就会触发[写时复制]。写时复制:在发生写操作的时候,操作系统就会去复制物理内存。
- 一般情况下子进程是不会进行写操作的,在重写的这个过程中,是不会影响主进程正常工作的,因为着可以进行正常的写操作。
- 如果这个时候主进程有一个修改操作,触发写时复制,操作系统将会将修改的这个key-value对应的内存数据复制一份出来,没有修改的数据父子进程还是共享的。
- 这个时候就会出现一个问题,主进程修改的key-value,因为触发了写时复制,子进程和父进程的内存数据不一致,这个时候就需要一个buffer缓冲区来解决问题。
- 当在AOF重写的过程, redis完成一条命令操作之时,同时将写个命令写入AOF缓冲区和AOF重写缓冲区中。
- 当子进程完成AOF重写工作之后,会向主进程发送一个信号通知主进程,异步进行。
- 主进程收到信号之后,会调用一个信号处理函数
- 会将AOF重写缓冲区中的内容追加到新的AOF文件中,使的新旧两个AOF文件的数据状态保持一致
- 将新的AOF文件进行改名,覆盖掉旧的AOF文件
总结:AOF的存储方式为命名式存储。在写入磁盘的机制有三种,可以通过配置去设置:ALways、Everysec、No三种机制各有优缺点,写入磁盘的时机也不一样。如果AOF文件大小如果大于64MB就会触发重写机制,会进行扫描数据中的所有键值对,对每一条数据生成一条写操作命令,将命令写入新的AOF文件中,重写完成后将新的AOF文件覆盖掉旧的AOF文件。在重写过中,由子进程进行完成。
RDB
- RDB内容为二进制数据
- reids通过
save和bgsave命令来生成RDB文件save由主进程生成RDB文件,由于和执行命令都在同一个线程,如果写入RDB文件时间过长,会造成阻塞bgsave是创建一个子进程来生成RDB文件,这样就不会影响主进程,不会发生阻塞。
- 命令
save time number- time秒之内,对数据库进行了至少number次修改。
- redis的RDB快照是全量快照,也就是说每一次进行RDB快照时候就需要将内存中的所有数据进行记录到磁盘中,如果如果进行频繁的RDB的时候,是一个很重的操作,对性能多多少少都是有影响的。如果我们设置的时间time是3分钟的话,如果发生宕机的情况就会丢失3分钟的数据。
- 执行快照的过程
- 这个时候使用bgsave创建一个子进程去进程。
- 一样也是fork创建子进程,复制主进程的页表给到子进程共享一片内存数据
- 如果这个时候父进程发生内存修改的操作就会触发写时复制。
- 发生修改的部分,物理内存会被复制一份。父进行会对这个复制出来的内存进行修改,不会影响到子进程的写入。
- 发生写时复制的数据为刚刚写入的数据,但是子进程的数据为旧数据,这个时候子进程是没有办法将主进程的新修改的数据进行RDB快照的,只能等下一次RDB快照的时候才行写入。
- 这里如果宕机的情况的下,就会丢失RDB前的数据
总结:RDB快照为二进制数据进行存储的,可以通过命令bgsave进行创建子进程进行生成RDB快照,在生成RDB快照的过程中如果触发了写时复制的情况,这个时候子进程是没有办法将新的操作记录下来的,也就是会丢失新修改的数据。
RDB AOF 混合模式
- 配置
aof-use-rdb-preamble配置成yes - 启用混合模式的情况下,fork出来的子进程会将与主进程的共享内存数据去以RDB方式写入AOF文件中,然后在这个过程中,主线程处理的操作命令会被记录到重写缓冲区中,重写缓冲区的增量命令以AOF方式写入到AOF文件中,这样就可以解决单独RDB会丢失的数据,所以文件中含有AOF和RDB两个内容,然后RDB和AOF会将旧的AOF文件替换掉。
- 注意前半部分为RDB内容,后半部分为AOF,因为RDB为二进制数据,相对来说快很多。
- 后半部分的AOF弥补了单独RDB可能丢失的数据。
- 这样一来性能上来说快很多,丢失的数据也少