初识Redis · 持久化
目录
前言:
RDB
AOF
前言:
本文开始,就已经算是Redis的进阶篇文章啦,因为从本文开始就要开始涉及到了Redis的一些特性和一些底层原理了。
那么首当其冲的,就是Redis中的持久化了,我们提及持久化之前,我们不妨回想一下MySQL中的事务有哪些特性,一共有4个是吧,分别是原子性,一致性,持久性,隔离性。对于MySQL中的持久性,因为MySQL中和硬盘打交道的,也就是说重启了服务器之后,数据依旧是存储在硬盘的,那么数据也就有了持久性的特点。而对于Redis就不同了,它是一共内存级别的数据库,也就是数据都在内存中,那么如何保证数据的持久性呢?
我们在平常使用的时候,其实已经发现了,当我们设置了一些键值对之后,我们退出Redis客户端,再重新登录的时候数据又有了,这其实就得益于Redis的持久化机制。
那么,本文将从两个角度,介绍Redis的持久化操作,这其实就是Redis的持久化机制,分别是RDB和AOF机制。
RDB
首先,我们要明确一个点是,既然要实现持久化操作,那么肯定要和硬盘打交道的,也就是说,Redis同时和内存硬盘打交道,不过代价也非常直观,消耗了更多的空间,不过因为硬盘价格便宜,所以开销的成本也是可以忽略的。
RDB的全名叫做:Redis database。
这个操作是定期把数据从内存备份到硬盘里面,生成对应的“快照”,当Redis重启的时候,就可以根据这个快照,恢复内存中的数据了。
那么这个定期恢复的定期来说,一般有两种方式,一种是手动触发,一种是自动触发,对于手动触发来说,涉及到了两个命令,分别是save和bgsave。
对于手动触发:
其中对于save来说,一旦执行了这个命令,那么Redis就会全力执行“快照”生成的操作了,那么如果Redis的数据量比较大,就会阻塞其他客户端的命令,这个操作就比较类似于keys *了,所以是不建议使用的。
其中对于bgsave来说,我们光从名字就可以发现,backgroud save,后台保存,它的意思就是Redis会在后台执行备份的这个操作。那么如何实现的呢?难道偷摸引入了一个线程?实际上并不是,Redis不是通过多线程的方式实现的,它使用的是多进程方式。
这个点其实还是比较让人惊讶的,在Redis的单线程玩久了,突然想起来还有一个进程,是吧。
就像是这样的。
对于自动触发:
就非常好理解了,Redis会根据对应的配置文件,每m秒数据的吞吐量达到了n,就会进行一次备份,这点,我们是可以在Redis的配置文件里面看到的。
而不管是手动触发还是自动触发,其实都会生成对应的快照文件,这个文件的路径我们也是可以在Redis的配置文件里面看到的。
当我们打开配置文件,在/etc/redis/的redis.conf里面,我们找到这一行,就能发现RDB的快照是存放在Redis的工作目录里面的,所以我们要找对应的快照,就要cd /var/lib/redis里面去看看。
在这里我们确实是发现了,那么我们使用vim打开看看:
我们也能在里面发现一些我们目前有的键值对的身影:比如lisi等。
那么我们清空了之后:
也确实发现了它里面的内容不见了。
这个时候同学就会说了,欸你不是说Redis有手动触发和自动触发吗?这也没看你手动触发啊,是不是因为自动触发了,实则不然,我们可以看看在配置文件里面的自动触发的配置:
配置文件里面有这么一段话,save the DB on disk,也就是说保存数据到硬盘上,它的基本格式是save second changes,也就是说多少秒发生了多少个键值对的改变,就会进行一次生成快照。
在默认的配置文件里面,是有三种规格,一次是15分钟动了一个键值对,一次是5分钟动了十个键值对,一次是一分钟动了10000个键值对。
上面的save ""的操作是不自动生成快照。
而在我们刚才的操作里面,虽然我们都不满足手动触发和自动触发的条件,但是我们退出客户端重新进入客户端的这个操作,Redis就会默认更新RDB文件了。
那么我们现在可以试试手动触发一下:
不管是save还是bgsave,对应的快照文件我们重新进入都会更新,并且我们也能发现一些我们刚设置的key。
那么这是对应的操作,我们能够发现对于dump.rdb文件来说,它里面的数据都是一些二进制文件,在存储的时候,就会通过压缩内存中的数据,保存到这个二进制文件中。
那么问题来了:
第一个:我们rdb文件是怎么生成的呢?是原地修改的还是重新创建一个再覆盖呢?
第二个:当我们创建一个key,而对应的dump.rdb文件没有及时生成,服务器突然挂了,新创建的key是否存在呢?
首先是第一个问题,对于save来说,这是阻塞式保存,Redis会停止其他服务,原地覆盖rdb文件,对于bgsave来说,采用子进程的方式重新创建,创建成功之后覆盖原来的rdb文件,所以inode会改变。那么问题来了,如果子进程保存快照的时候,父进程仍然在输入数据,恰好父进程突然挂了,那么新的数据就没有了,这个怎么办?
它的解决方法就是使用AOF或者主从复制等了,这个后面细说了。我们现在先来验证bgsave的机制:
对于第二个问题,我们的操作是创建一个key,然后异常关闭服务器,再连接,这个时候服务器就相当于断电了,那么对应的rdb文件就没有正常生成,所以我们查询不到对应的数据。咱其实日常基本都是正常关闭,但是我们最怕的就是异常关闭,比如kill这种。
而对于rdb文件来说,我们要记住旧版本的rdb文件不一定能被新版本的redis识别。
不知道有没有坏心思的同学,既然我们能够随便查看对应的rdb文件,我们是否能够更改呢?
当然是可以的,但是我们要记住一点,rdb文件如果出现损坏,redis服务器可能就起不来了,那么是不是我们只要不动它就不会损坏呢?那不见得,某些操作,比如网络传输都是由可能导致它出错的。
我们现在来恶意损坏试试:
此时我们已经损坏完成,然后我们重启一下redis服务器:
此时我们使用redis-check-rdb程序就可以判断出对应的rdb文件出错了。那么我们的解决方案可以是直接给rdb文件删除了,或者使用之前备份过的。
此时我们的RDB就简单的算完成了,我们可以简单的总结一下。
RDB是服务器通过生成快照的方式进行持久化操作的一种机制,涉及到的命令有save和bgsave,其中save是阻塞式保存,bgsave式后台子进程+覆盖rdb文件的方式保存,涉及到的检查工具有redis-check-rdb,因为redis是内存数据库,所以在服务器没有挂的时候我们直接退出客户端,也不save或bgsave的,也能get到对应的数据,我们复习的时候也能通过这个问题来引入RDB操作。
AOF
那么我们通过对RDB操作的理解,我们发现RDB的方式是用来存储数据来保证数据的持久化的,那么数据怎么来的?数据实际上是我们使用各种操作而来的,那么有没有一种操作是通过保留操作来持久化数据的呢?
对于AOF(Append Only File)来说,它的操作就是通过保留操作来进行持久化的,它就可以解决RDB不能实时保留数据的缺点了,对于AOF来说,它默认是关闭的,当我们开启AOF操作的时候,启动的时候也就不再读取RGB文件了。
我们首先现在配置文件里面把AOF打开并且重启服务器:
并且我们也看到了对应的文件名叫做appendonly.aof,它的位置还是在/var/lib/redis中。
当我们插入了数据之后,也确实看到了里面记录着一些我们的操作。那么现在有一个问题了,Redis既然是一个单线程服务器,写入内存的同时还要兼顾到硬盘的AOF文件,是否会降低它的速度?
答案是不会的,因为它并不是说你执行一个命令就往aof文件里面写入,它的操作是存在一个内存缓冲区,积累到一定程度就写入,所以会效率的影响不是很大,并且如果是在硬盘随机读写的话,速度是较慢的,但是好在Redis对AOF文件是末尾写入,也就是顺序写入,所以并不算慢。
对于AOF文件的写入机制主要还是因为这句话:写入硬盘的数据量并不能决定效率,而是写入次数。
那么同样还是服务器突然挂掉的情况,AOF如果没有及时写入,也是会丢失对应的操作数据的,这个时候Redis就给出了一些选项,让我们根据情况取舍,在次数和数据量之间进行平衡。
配置项 | 值 | 含义说明 | 写入频率 | 数据安全性 | 性能影响 | 适用场景示例 |
---|---|---|---|---|---|---|
appendfsync | always | 每次写命令都立即同步写入 AOF 文件 | 每条命令 | 最安全 | 性能最差 | 对数据极为敏感的金融或订单类系统 |
appendfsync | everysec (默认) | 每秒同步一次,可能丢失 1 秒以内的数据 | 每秒一次 | 较高 | 性能较好 | 大多数业务场景,如缓存、常规数据库替代 |
appendfsync | no | 仅由操作系统控制何时将数据刷新到磁盘 | 不固定 | 较低(可能丢较多数据) | 性能最佳 | 数据不重要的缓存场景,或自己手动控制持久化 |
同样,我们也是能在配置文件里面进行修改。
那么,当我们试想一下,如果我们set了一个key,又del了,我们是否有记录它的必要?显然没有,这就是AOF的重写机制。它会根据实际情况进行重写。
同样还是fork一个子进程,然后子进程继承了父进程当前的数据,所以子进程就会按照AOF的文本要求,将内存中的数据记录到新的AOF文件中,当然了,新的数据操作父进程也不会闲着,父进程会将新的数据操作记录到aof_rewrite_buf这个缓冲区里面,然后子进程写完了之后会通过信号通知父进程它写完了,这个时候再父进程把aof_rewrite_buf的数据合并到新的AOF文件中。这个时候就可以用新的AOF代替旧的。
不过这个旧的AOF文件可是不敢删除的,并且还是要写的,万一服务器突然挂了,新的AOF文件不完整,还能有个旧的AOF文件兜兜底。
这里我们可以简单的区分一下AOF和RDB的差别了,RDB对fork之后的数据就不管了,AOF通过缓冲区的机制会实时保存,这里其实就是RDB定期备份和AOF实时备份的一个差别了,当然,我们还是要通过实际情况应用的。
当AOF和RDB文件同时存在的时候,因为AOF文件信息更全,所以是以AOF为主的。
可是当重写数据的时候,因为文本方式写入开销有的时候挺大的,Redis就引入了混合持久化的操作,即Redis在重写的时候,子进程会将父进程当前的内存数据按照RDB的方式,即二进制的方式写入到AOF文件中。
这就是混合持久化,我们可以验证一下。
首先我们要开启混合持久化的配置选项:
使用命令bgrewriteaof进行重写。然后再进行一些数据操作
我们就能发现确实AOF文件变成了我们想要的样子。
特性 | AOF(Append Only File) | RDB(Redis DataBase) | AOF 更全的理由 |
---|---|---|---|
持久化内容 | 每条写命令(SET、DEL 等) | 某个时间点的内存数据快照 | 记录了所有写入“过程”,不是只看最终值 |
触发方式 | 每条命令 / 每秒 / 系统控制 | 手动 SAVE / 自动定时 SAVE | AOF 写得更频繁,更新更实时 |
丢失数据可能性 | 极低(可每秒甚至每次写入刷新) | 有(取决于快照频率) | AOF 通常最多丢失 1 秒内数据,RDB 可丢分钟数据 |
文件大小 | 大,命令多,冗余高 | 小,只含最终结构数据 | AOF 为了完整性记录全部操作 |
恢复速度 | 慢(执行命令) | 快(直接加载) | 尽管慢,但更精细 |
内容可读性 | 高(Redis 命令文本) | 低(二进制格式) | 可直接编辑、分析、还原或回滚 |
手动修复灵活性 | 高,可编辑命令以排错或回滚 | 低,损坏则难以修复 | AOF 可删除误操作命令,回滚到期望状态 |
适用场景 | 数据极其重要、希望尽量减少数据丢失 | 对性能要求高、容忍短时数据丢失 | AOF 更适用于对数据恢复完整性要求高的系统 |
以上算是一个总结。
感谢阅读!