Redis持久化
Redis相较于MySQL等数据库的操作速率更快,主要是因为它是在内存上的操作;而MySQL这种结构型数据库是在硬盘层面上的操作,总所周知运行速度:CPU>>内存>>硬盘。但是另外一方面只要是稍微懂点硬件基础知识的都知道,内存是断点即失的,那一断电或者服务器一宕机数据就丢失的话,Redis是否有些太不安全了,它是如何解决这方面问题的呢?主要有如下两种方式。
1. 方式一:快照(snapshot)
这也是Redis官方默认并且推荐的持久化方式,快照就是把这一时刻下Redis服务器中存在的数据情况记录存储下来,生成一个.rdb文件,当服务器断电或者宕机数据丢失以后,执行这个.rdb文件就可以恢复此前时刻记录下的数据和结构。
快照有如下两种触发方式:
1.1 符合配置文件中的条件
在redis.conf文件是redis的服务配置文件,其中就有关于快照触发条件的设置
含义分别是:900秒(15分钟)内数据更改达到1次,生成一次快照;300秒(5分钟)内数据更改达到10次,生成一次快照,60秒(1分钟)内书籍修改达到10000次。快照生成触发一次,三个条件满足一条即可。这种触发生成快照的方式也是默认开启的,不需要用户进行修改。
1.2 客户端执行SAVE/BGSAVE指令
客户端执行了BGSAVE指令之后,redis会调用fork来创建子进程,父进程继续处理命令请求,子进程和父进程初始公用同一块内存,当父进程接受到修改请求以后,子进程和父进程的内存共享结束。这样的执行方式能够在父进程数据没有被修改的情况下使用尽可能最大的内存复制数据,当父进程需要修改数据之后,子进程剥离出来,也不会对父进程造成拥堵。
客户端接受到SAVE指令之后,redis服务器在创建完成快照文件之前不再接受响应任何其他指令,避免对数据修改造成数据的不一致性。同时当服务器接受到SHUTDOWN指令之后,还会执行一条SAVE指令,保存服务器最后时刻状态保存的数据。
2. 方式二:AOF(Append Only File)
在方式一中,数据是存在一定的延时性的,如果服务器接收到一次写操作,但是还没有触发下一次快照生成条件的情况下,此次写操作就不会被记入到.rdb文件中,所以当服务器突然崩了以后,.rdb中的文件是可能存在很大的滞后性的。由此有了第二种持久化方式,追加日志文件。redis会将所有的写操作命令写入到appendonly.aof(默认,可修改)文件中,这样redis就能记录下所有的数据更新操作,当服务器重启之后只需要把aof文件从头到尾执行一遍就能恢复完整数据。这种方式默认是关闭的,将no改成yes开启。
可以看到这种方式redis默认是不开启的。同时这种方式也有三种触发条件
always(不推荐):顾名思义,服务器只要执行一次写操作,我就记录一次,这种方式会极大的占用redis主进程的内存空间,降低服务效率。
everysec(推荐):每一秒执行一次,会由子进程调用SAVE指令,如上文介绍的,不会阻塞服务。
no(不推荐):这里不是不执行的意思,是服务调度交给了系统层面,系统什么时候调用就什么时候保存,系统一直不调用就一直不保存,数据容易大量丢失。
2.1 文件重写
aof文件中记录的是所有的写操作,如果是重复对同一个key写多次,aof也会把所有写操作记录下来,显然这种记录方式会让aof文件十分臃肿,同时当使用aof恢复数据的时候也会有很多没有必要的操作过程,我同一个key修改了100次我当然只需要执行第100次写入即可,但是你却执行了100次来恢复这一条数据,就很浪费时间和空间,这时候就需要重写机制了。它的触发方式有两条规则:
此次写入的文件大小达到前一次写入的两倍的时候同时文件超过了64mb文件大小限制以后执行重写。这两个规则就是用来限制重写频率的,当文件比较小的时候代表重复的写语句不多或者是数据量不大,重写的必要性低;当数据量大的时候重写容易造成主线程阻塞。
文件重写不是去读前一次的aof文件然后筛选去重保留最后一次执行的语句,是筛选不出来的,重写执行的操作是去查看当前记录了哪些数据,把这些数据当成写操作,把写操作语句记录到新文件中,当所有数据复制完成之后替换覆盖旧文件。