"在哪里走散,你都会 找 到 我。"
我们在接触Mysql事务的时候,一定了解过Mysql事务的四个特性:
"原子性(A)一致性(C)隔离性(I)持久性(D)"
而其中持久性其实与持久化是一回事,所谓持久与不持久,针对的是数据。如果将该数据存储在磁盘上,那么数据就具有持久性,反之如果数据仅仅存在于内存上,就不具有持久性。
对于Redis而言,是一个内存数据库,操作的数据大都是内存级别的(Redis相比于Mysql明显优势和 特点)。但在内存存储数据是不持久的,一旦断电内存中的数据就会被清空,要想持有持久化的特性,数据必须存储在磁盘上。
所以,既要保证速度快,那么数据的操作一定还是在内存,但如果要保证持久化,数据还是得想办法存储在磁盘上。Redis决定全都要!
可是内存与硬盘上存储的数据只是存在理论上的一致性,实际中可能存在一定的偏差,这同我们选择的 "持久化策略" 是息息相关的。
也许你就会有疑问,如果此时要想redis插入或者获取一个数据,是在内存、是硬盘上操作呢?
当要插入一个数据时,就需要把这个数据同时写入内存、硬盘之中的!但说是这样说,何时写入硬盘,效率咋样,都是同我们选择的策略有关的。
如果是要查询一个数据,直接选择从内存中读取即可。硬盘数据仅仅是在redis进入重启时,用来恢复原来内存中数据的。
严格来说redis中的持久化策略有两种:
● RDB -- Redis DataBase
● AOF -- Append Only File
其中你可以把RDB策略理解为一种"定期备份",它一定带来的一个问题是实际内存值与备份值可能存在偏差。AOF策略可以理解为一种“实时备份”。
RDB持久化就是将当前进程中的数据,定期写入到磁盘中,生成所谓的“快照”,也是Redis默认的持久化机制。
● 手动触发
程序猿通过redis客户端执行特定的命令,触发生成快照。
save: 执行save命令时,会导致redis全力以赴生成“快照”,因为redis采用的是单线程模型,此时就会阻塞redis处理其他客户端的命令。(出现类似keys*的后果,一般不建议使用save)。
bgsave: 不会影响redis处理其他客户端的命令。但,redis咋做到不需要多线程完成并发编程的?答案是这里使用的是多进程模型,来处理并发编程。
● 自动触发
在Redis配置文件中设置了让Redis,每隔多长时间生成\每产生多少次修改,就会触发生成快照。
redis的配置文件通常会在/etc目录下的redis.conf文件。
我们打开redis.conf文件后,可以找到redis生成的rdb文件是存放在redis的工作目录中的,这个是可以在redis中进行配置的。
我们进入这个路径就可以看到这文件名为 "dump.rdb"的文件。
当我们打开这个文件时,我们会发现是一堆乱码。
dump.rdb本质是一个二进制文件,是将内存中的数据,以压缩的形式保存在这个二进制文件之中的。我们一定不要拿着vim,将把这个rdb文件里的内容进行乱改。因为redis每次重启都会尝试加载这个rdb文件,如果发现这个文件格式是错误的,数据加载也会出现错误。
当然,我们认为不去修改并不是唯一可能导致rdb文件出现损坏的情况,当把rdb文件进行网络传输,仍然可能造成内容缺失,引起文件破坏,此时可能导致redis服务器无法启动。
当执行生成rdb文件镜像时,此时会先将要生成的快照数据保存在一个临时文件之中。当这个快照生成完毕后,会删除之前的rdb文件,并把这个临时文件重命名为dump.rdb,因此从始至终都只有一份rdb文件。
rdb文件的数据,不会根据你简简单单插入一些数据就会发生更改。其原因就在于没有触发RDB机制。
在前些段落提到过rdb有两种触发机制,分别是自动和手动触发。自动触发机制主要是在配置文件中,需要去手动进行修改。
当然,这些值不能设置得过小,导致频繁触发生产快照,而每一次快照生成的成本也不是可以忽略的。正因为不能频繁生成快照,所以rdb文件里的数据与实时数据可能会存在偏差。
甚至,如果redis服务器直接挂掉(例如使用kill命令等)。会导致在上一个快照版本,与服务器崩掉前期间的数据都丢掉了!这不是rdb能够解决的问题。
手动执行命令save\bgsave,因为咱们的数据量是很少的,所以一旦执行这两个命令中的其中一个都会很快的产生结果。感受子进程创建的过程,对我们来说比较奢望,但是检查生成的新文件,对我们而言却十分容易。
Linux文件系统是基于ext4的组织方式,将文件系统分为三大组成部分:
▪ 超级块:放的是一些管理内容。
▪ inode区: 存放inode节点,每一个文件都有唯一的inode数据结构进行对应。
▪ block区:存放文件的数据内容。
• RDB是⼀个紧凑压缩的⼆进制⽂件,代表Redis在某个时间点上的数据快照。
• Redis加载RDB恢复数据远远快于AOF的⽅式。RDB使用二进制的方式来组织数据,直接把数据读取到内存中来,按照字节格式取出来即可。但是AOF是用文本方式来组织数据,需要一系列的字符串切分操作。
• RDB⽅式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运⾏都要执⾏fork创建⼦进程,属于重量级操作,频繁执⾏成本过⾼。
• RDB⽂件使⽤特定⼆进制格式保存,Redis版本演进过程中有多个RDB版本,兼容性可能有⻛
险。
当然RDB最大的问题还是在于,无法实时持久化保存数据,在两次快照之间,如果服务器出现差错,数据会出现丢失。
AOF(AppendOnlyFile)持久化:以独⽴⽇志的⽅式记录每次写的命令,重启时再重新执⾏AOF⽂件中的命令达到恢复数据的⽬的。
“AOF的主要作⽤是解决了RDB数据无法持久化的问题”。
因为redis默认使用将RDB作为默认的持久化机制,开启AOF功能需要设置配置:
该appendonly.aof文件同rdb文件一样,存在于工作目录。
AOF本质上是一个文本文件,可以类似于Mysql中的binlog,记录用户的一系列操作。其中会用到一些特殊符号作为分隔符。
▪ 手动触发:
调⽤bgrewriteaof命令
根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定⾃动触发时
机。
auto-aof-rewrite-percentage:代表当前AOF占⽤⼤⼩相⽐较上次重写时增加的⽐例.
auto-aof-rewrite-min-size:表⽰触发重写时AOF的最⼩⽂件⼤⼩,默认为64MB.
对于RDB而言,生成的快照只是对当前内存中数据的一份镜像,它一定存在数据不具有实时性的问题。由此,引入AOF后,既需要对内存进行写入,又需要对磁盘进行写入,才能保证数据的实时性。这会产生一个问题:
答案是否定的!
AOF的工作机制并非把工作线程的数据直接就写在磁盘上,而是会在内存中开辟空间,用作缓冲区,在积累了一波后,才会向硬盘统一写入这一份数据。
硬盘内容的读取是根据磁头寻址的方式,如果内容数据是随机地址,那么查询花费的时间是很高的,效率也是低下的。相反,如果是顺序写入,磁头的读取效率是比随机读取要高很多的(虽然仍比不过内存的读取速度)。
AOF是把每次新的操作写入到原文件的末尾,这是顺序写入。
将数据写入在内存开辟的缓冲区中,其数据存储还是在内存中的,一旦断电、或者崩溃这些数据还是会发生丢失!这是不可避免的!
redis为程序猿给出了一些选项,也就是一些刷新策略(缓冲区)。
可配置项 | 说明 |
always | 命令写入aof_buf后,调用fsync同步,完成后返回 |
everysec | 命令写入aof_buf后,只执行write操作,不进行fsync。由同步线程每秒进行fsync |
no | 命令只执行aof_buf,由OS控制fsync频率 |
我们谈到访问外设速率,并不强调访问或读取速度,而是次数。
▪ 当刷新频率越高,对性能影响越大,同时数据的可靠性越高。
▪ 当刷新频率越低,对性能影响越小,同时数据的可靠性越低。
随着命令不断写⼊AOF,⽂件会越来越⼤。
较⼩的AOF⽂件⼀⽅⾯降低了硬盘空间占⽤,⼀⽅⾯可以提升启动Redis时数据恢复的速度。
● 如果当前进程正在执⾏AOF重写,又接收到AOF请求直接返回。如果是当前进程正在执⾏bgsave(RDB)操作,重写命令延迟到bgsave完成之后再执⾏。
● ⽗进程执⾏fork创建⼦进程。
● 重写
1.主进程fork之后,继续响应其他命令。所有修改操作写⼊AOF缓冲区并据appendfsync策略同步到硬盘,保证旧AOF⽂件机制正确。 如果重写中断,能够恢复出原数据。
2.⼦进程只有fork之前的所有内存信息,⽗进程中需要将fork之后这段时间的修改操作写⼊
AOF重写缓冲区中。从而保证数据的实时性。
● ⼦进程根据内存快照,将命令合并到新的AOF⽂件中。
● ⼦进程完成重写
1.新⽂件写⼊后,⼦进程发送信号给⽗进程。
2.⽗进程把AOF重写缓冲区内临时保存的命令追加到新AOF⽂件中。
3. ⽤新AOF⽂件替换⽼AOF⽂件。
顾名思义,混合持久化要做的既是要保证实时性,又得保证效率。结合aof和rdb的特点。
按照aof的方式,对每一个请求、操作都记录到文件里,触发aof重写后,就会把内存的当前状态形成快照,按照rdb的存储格式写入到aof新文件里。后续再进行操作,会按照aof文本的方式追加到文件后面。
当redis启动时,会根据aof或者rdb文件进行数据恢复。但,如果两者文件都存在,那么redis会如何选择呢?
rdb对于fork之前的数据会进行备份,但对于fork之后的数据则会置之不理,不会关心之后的任何数据。而对于aof,关心fork之前的数据意外,还能通过aof_rewrite_buf换从化区获取fork之后新数据。
rdb本身的设计理念是“定期备份”,而aof设计的理念在于“实时备份”。
● RDB视为内存的快照,产⽣的内容更为紧凑,占⽤空间较⼩,恢复时速度更快。但产⽣RDB的开销较⼤,不适合进⾏实时持久化,⼀般⽤于冷备和主从复制。
● AOF视为对修改命令保存,在恢复时需要重放命令,持久性安全性高。并且有重写机制来定期压缩AOF⽂件。但其加载速度是不及RDB的。
● RDB和AOF都使⽤fork创建⼦进程,利⽤Linux⼦进程拥有⽗进程内存快照的特点进⾏持久化,尽可能不影响主进程继续处理后续命令。
本篇到此结束,感谢你的阅读。
祝你好运,向阳而生~