欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Redis 持久性

最编程 2024-10-17 07:34:55
...

认识持久化

在MySQL的事务中,有四个核心的属性:

1.原子性

2.一致性

3.持久化

4.隔离性。

其中这个持久化就是指将数据存储到硬盘上,进程在重启 / 主机重启之后,数据依然存在。

然而Redis是一个内存数据库,但是它也有持久化的策略。

Redis ⽀持 RDB 和 AOF 两种持久化机制,持久化功能有效地避免因进程退出造成数据丢失问题,
当下次重启时利⽤之前持久化的⽂件即可实现数据恢复。

RDB:Redis DataBase 。主要是采取一个定期备份的策略。

AOF:Append Only File。主要采取实时备份的策略。 

RDB机制 

 RDB的触发时机

RDB会定期把Redis的所有数据都写入到硬盘中,生成一个 “快照”

如果Redis一旦重启了,就可以根据之前的快照把数据恢复出来。

定期触发具体来说又有两种方式:

a.手动触发 :

程序员通过Redis客户端,执行特定命令的方式,来触发快照的生成。

这里又有两个命令:

1.save命令:

执行save命令,Redis就会执行快照生成的操作,此时就会阻塞Redis的其他客户端的命令,导致类似于keys * 的后果。一般不建议使用save。

2.bgsave命令:

bg => background(后面) ,bgsave不会影响Redis服务器处理其他客户端的请求和命令。

此处Redis是用多进程的方式来完成并发编程,也就是bgsave的实现。

b.自动触发

在Redis的配置文件中,设置Redis每隔多少时间 / 每产生多少次修改 就触发。

RDB bgsave的执行流程

大致执行流程顺序 :

1.先判定当前是否有其他的子进程正在执行bgsave,如果有,那么直接把bgsave返回。

2.如果没有其他正在工作的子进程,那么就通过系统调用fork来创建一个子进程。

3.子进程负责进行写文件,生成快照的过程中,父进程正常接收处理客户端的请求和命令。

4.子进程完成持久化的操作后,就会通知父进程(以信号的方式),此时父进程就会更新一些统计信息,然后子进程就可以结束销毁了。

RDB的镜像文件

Redis生成的rdb文件,是存放在Redis的工作目录中的,这一点在配置文件中可以设置。

Redis的配置文件目录:

ls /etc/redis/

在配置文件中

上面这个就是rdb文件的默认名字。 

这个目录就是 rdb文件 的存放目录,也就是Redis的工作目录。

进去之后,确实有一个 dump.rdb文件。

 这个文件不要乱改,不然文件格式出错的话,就会导致Redis服务器无法启动或者其他的情况。

另外Redis还提供了 rdb文件的检查工具,先进入

cd /usr/bin/

然后

ll redis*

其中,这个redis-check-rdb就是检查工具了。

关于rdb文件

rdb的持久化操作是可以触发多次的,当执行生成rdb镜像的时候,就会把要生成的快照数据存放到一个临时.rdb文件中,然后再把原来的dump.rdb文件删除掉,然后将临时文件改名为durm.rdb。 

RDB的演示效果 

 我们知道RDB的触发方式有两种:

1.手动触发(命令 save,bgsave)

2.自动触发(配置文件进行设置)

关于自动触发,我们在配置文件中可以找到如下:

上面是对save的描述,大致意思就是

比如 save 900 1表示的就是每900s,执行了一次修改,那么就会进行一次触发,

save 300 10 表示的是每300s内,执行了10次修改,就触发一次RDB。

这是关于自动触发的,我们可以修改这些数值,但是要有一个基本的原则:

生成一次RDB快照的成本是比较高的, 不能让这个操作过于频繁。

在save 60 10000可以知道,两次生成RDB快照的最短间隔是60s,如果中间60s如果Redis服务挂掉了(机器断电,设备故障),那么中间的数据就丢失了,而AOF就是解决这个问题的有效方案。

再来说自动触发,这里推荐使用 bgsave命令。

先看dump.rdb文件,虽然它是二进制的方式存储的

vim dump.rdb

我们在Redis客户端插入一些数据并保存 

此时重新打开dump.rdb文件,就会发现不一样了。

 仔细看,多了key1 key2这样的字符。

重启服务器之后,发现之前的key都还在。

停止redis-server

service redis-server stop

然后再启动

service redis-server start

 

这里发现我的数据就被恢复上来了。

 另外,我们之前也提到过,RDB不仅可以手动触发,还可以自动触发:

1.比如刚刚展示的配置文件,达到条件时就会自动触发。

2.通过shutdown命令(Redis的命令)关闭Redis服务器时,也会触发。或者Ubuntu下 service redis-server restart这样正常关闭的方式也会自动触发。

3.Redis进行主从复制的时候,主节点也会自动生成快照,然后把快照传输给从节点。

关于dump.rdb文件的替换,因为我们当前的数据量太少了,所以bgsave几乎是瞬间完成的,所以很难观察到替换的过程。

我们可以通过观察原dump.rdb的Inode编号的方式来判断是否发生了替换

使用stat命令即可观察

stat dump.rdb

执行一次bgsave之后

我们发现Inode变了,说明发生了替换。

注意:如果是save命令,那么是不会触发 子进程 & 文件替换的逻辑的,而是会覆盖式的修改。 

当我们执行flushall命令时,也会清空rdb文件的。

自动生成快照的功能也是可以在配置文件中关闭的。

如果rdb文件坏了会咋样?

如果我们手动的把rdb文件改坏了,然后Redis服务又是非正常关闭的(kill杀掉了),那么此时产生的后果就是不可预期的。有可能Redis可以启动,但是数据可能会有问题,或者还有可能Redis直接启动不了了。

当Redis服务挂了后,我们可以看看Redis的日志,看看发生了什么

日志的目录

ll /var/log/redis/

 这个位置也是可以在配置文件中修改的。

其中.log就是日志文件。

之前也说过,Redis提供了rdb的文件检测工具

执行

redis-check-rdb dump.rdb

 

我这里是没有问题的。

RDB小总结:

RDB 是⼀个紧凑压缩的⼆进制⽂件,代表 Redis 在某个时间点上的数据快照。⾮常适⽤于备份,全量复制等场景。⽐如每 6 ⼩时执⾏ bgsave 备份,并把 RDB ⽂件复制到远程机器或者⽂件系统中如 (hdfs)⽤于灾备。

Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式。

RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进程,属于重量级操作,频繁执⾏成本过⾼。

RDB ⽂件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有⻛
险。

RDB最大的问题就是不能实时的持久化保存数据,两次快照生成期间,实时的数据可能会伴随着重启而丢失。 

AOF机制 

关于AOF的基本操作

 AOF默认是关闭状态,可以在配置文件中修改。

当开启AOF时,RDB就失效了。

AOF类似MySQLDbinlog,会把用户的每个操作都记录到文件中,当Redis重新启动时,就会读取这个aof文件,用来恢复数据。

因为aof文件是以文本的方式存储的,相比于用二进制存储的rdb文件,aof文件的读取速度要慢一些。

我们可以在配置文件中查找关于AOF的选项

关于vim的查找

我们可以在普通模式下(按Esc进入),输入 / + 需要查找的文本。按 n 可以进入下一个匹配项。

示例:

这里发现默认是no,也就是关闭的,我们可以修改成yes。

下面是aof的默认文件名。

修改成yes后,我们重启Redis服务器

然后再看看aof文件

发现可以看到我们设置的key1 key2。

AOF缓冲区的刷新策略

AOF对Redis的性能影响大吗? 

直接先说结论:影响不是很大。也取决于AOF的刷新硬盘的策略。主要原因有两点:

1.AOF机制并不是直接让工作线程把数据写入到硬盘的,而是在内存上有一个缓冲区,会先把数据写入到缓冲区中,当缓冲区中积累了一定的数据时,再统一写入到硬盘中。

2.硬盘上读写数据顺序读写的速度还是比随机访问的速度要快很多的(当然还是比内存慢很多),AOF是每次把新的操作写入到文件的末尾,属于顺序写入。

到这里我们注意到这个缓冲区是在内存中的,那么如果此时数据还没来得及刷入硬盘,进程就挂掉或者机器掉电了,那么数据还是会丢失的。

此时Redis就会给出一些选项,让程序员自行选择缓冲区的刷新策略。

刷新频率越高,数据的可靠性就越高,但是性能影响的也就越大。反之。

Redis提供了三种策略。 

第一种是频率最高的,一有命令就会刷新。

第二种就是每秒刷新。

第三种就是完全把刷新交给系统,比如缓冲区满了,或者进程正常退出了。

在配置文件中 

我们发现默认就是第二种。

AOF的重写机制 

重写是什么?为什么要重写?
因为AOF的最终目的就是为了保存Redis最后在内存中的状态,那么Redis在中间是什么样的状态我们其实并不关心,我们只关心Redis最终是什么状态。于是以最终状态的视角来看,aof文件中可能就会存在一些冗余的操作,比如:

随着aof文件的体积不断增大,就会导致下次启动Redis的时间越来越长。所以Redis可以通过剔除这些冗余操作的方式,来使得 aof文件达到 “瘦身” 的效果。

那什么时候会重写呢?

这里也分为手动触发和自动触发:

手动触发就是调用bgrewriteaof命令。

关于AOF重写的流程:
这里跟RDB的bgsave类似,也是通过fork创建子进程的方式。

父进程仍然接收请求,而子进程负责针对aof文件进行重写。

子进程重写的时候, 不关心原来的aof文件中有啥,它只关心内存中最终的数据状态。

子进程只需要把当前内存中的数据获取出来(这里的数据已经是剔除掉冗余操作后的数据了),以AOF的格式写入到一个新的aof文件中,写入完成之后,再替换掉原来旧的aof文件。

不过在这里还有一个需要注意的是,在父进程fork出子进程之后,此时子进程就继承了当前父进程的内存状态,但是在子进程重写AOF的时候,父进程正常接收请求,并且在创建完子进程之后,就会再准备一个 aof_rewrite_buf缓冲区,此时收到的请求会在 aof_rewrite_buf上写一份,还会在原来的aof_buf上写,并且aof_buf依旧会遵循原来的刷新规则。等子进程重写完毕之后,就会以信号的方式通知父进程,父进程再把aof_rewrite_buf中的数据写入到新的aof文件中,然后再替换掉旧的aof文件,此时重写才会结束。

到这里有些人可能就会疑惑,为什么父进程要把数据写两份呢?为什么不直接把数据写到aof_rewrite_buf,然后再追加到新的aof文件中不就行了吗?

其实这里不写还不行,因为要考虑到极端情况:假设子进程还在重写的时候,进程就挂了或者机器掉电了,那么子进程内存中的数据就会丢失,新的aof文件的数据就不完整,如果此时父进程没有坚持写旧的aof文件,那么重启后就无法保证数据完整性了。 

关于AOF重写的其他问题:

a.如果手动重写 也就是执行bgrewriteaof命令时,Redis已经进行重写了,那么会咋样呢?

那么此时Redis不会再进行重写了,会直接返回。因为重写也没什么意义。

b.如果在执行bgrewriteaof时,发现当前Redis在生成rdb快照,会咋样呢?

此时AOF重写操作就会等待,等待rdb快照生成完之后,再执行AOF重写。

 

持久化总结

AOF机制和RDB机制各有各自的优缺点,在Redis中,默认是采取混合持久化的方式的。

简单概括就是:

RDB机制效率高(还包括了服务器的启动速度,因为是二进制数据),但是不能实时持久化。

AOF可以实时持久化,性能相对于RDB较低。 

可以在配置文件中找到

 

所谓混合持久化就是结合了AOF和RDB的特点。

开启AOF后,Redis会把每一个操作写入aof文件中,但是在触发重写之后,Redis就会按照当前的内存状态,把数据以RDB二进制的格式写入到新的aof文件中,后续再进行操作时,又仍是按照AOF文本的方式追加到文件后面。

并且,当aof文件和rdb文件同时存在的时候,Redis启动时就会优先读取aof文件。

原理到这里我们也都清楚,因为aof文件包换的数据比rdb文件的要更全。