Redis持久化:
前面我们讲到mysql事务有四个比较核心的特性:
- 原子性:保证多个操作打包成一个。
- 一致性:A给B100,A少一百,B必须多一百。
- 持久性:针对事务操作必须要持久生效,不管是重启还是什么数据是否还在,mysql把数据存储在硬盘上。
- 隔离性:脏读幻读不可重复读等等。
Redis是内存数据库,把数据存储在内存上,要想能够做到持久,就要把数据存在硬盘上面,为了速度快,数据得存在内存上,但是为了持久,数据还得存在硬盘上面。那么我们就将数据存在内存和硬盘上面。但是查询的时候就直接从内存中读取,硬盘的数据是为了重启后放在内存中的。
RDB(Redis Database)持久策略:
RDB是定期将数据写入内存中,都给先写入硬盘中,形成一个快照,也就是redis把内存中的数据,快速“拍个照”,存储在硬盘中,就能在重启的时候把数据回复过来。
- 手动触发:程序员通过自己写代码来执行特定的命令,来触发快照的生成,save和bgsave(如下介绍)等等。
- 自动触发:在配置文件中配置多久触发一次自动存储。
save:redis全力执行快照生成的操作,有可能阻塞redis。一般不建议使用。
bgsave:不会影响redis处理其他任务。既可以保证持久化顺利进行,也不会影响redis处理其他请求和命令。创建子进程,子进程完成持久化操作,持久化操作将数据写入RDB文件中,接着用新文件替换旧文件。
- 判定是否存在,如果已经存在其他进程运行了,bgsave就会直接返回,并不会继续执行。
- 如果不存在的话就会通过fork的方式创建进程,fork创建新的进程简单粗暴,就是复制一份和父亲一模一样的进程,一旦复制完了就是两个独立的(一模一样)进程。也就是安排子进程进行复制操作。
- 子进程进行写文件,父进程继续接受客户端请求。
- 子进程完成持久化的过程,通知父进程,父进程更新一些统计信息,子进程销毁。
如果子进程和父进程的数据一样就不会触发拷贝,但是如果有变化就会触发写时拷贝自己原本的数据。
redis生成的RDB文件是存放在redis的工作目录中的,也就是下面这个文件,redis会在工作的时候把输出的等等中间文件放到这里边。
在该目录下面查看文件,找到文件dump.rdb镜像文件。该文件是二进制的文件,以压缩的方式保存下来(节省空间)。
当执行RDB镜像操作时候,就会把要生成的文件先保存到一个临时文件中,当我们的快照生成完毕的时候,再删除之前的RDB文件,把新生成的文件名字修改成我们的dump.rdb。能够保证至始至终的RDB文件只有一个.
RDB文件并不是马上插入就会触发,是通过手动触发或者自动触发。
在redis的配置文件中阐述了自动触发保存的相关信息,虽然这里都可以随意修改数据,但是我们修改的时候要有一个基本原则,这里的RDB是一个比较高的成本,不能让这个操作比较频繁。
问题:如果在两次快照(两次存档点)之间有大量的key插入删除和修改,但是在两次快照之间服务器突然挂了,就会出现数据丢失的情况!!!
自动触发:
自动触发的场景有两种:
- 1.超时时间后自动触发保存。
- 2.通过shutdown命令关闭redis服务器就会触发自动保存。
- 进行主从复制的时候,主节点也会生成RDB快照,把RDB快照传给后面的从节点。
实际上如果当redis异常关闭的时候,来不及生成RDB,就不会保存key存在RDB中。
使用linux的stat命令来观察var/lib/redis(在redis.conf中)文件下的修改rdb文件的incode编号来观察修改,如果再次执行bgsave的话,inode编号就会发送变化。
如果是save命令就在当前进程中直接写入dump.RDB文件中写入数据,也就不存在文件替换等过程了。
自动生成RDB快照:
在配置文件中修改,修改自动生成RDB快照。
如果是save加上空字符串,那就是关闭快照 。
RDB文件改坏了:
在etc/redis目录下打开/var/lib/redis查看vim dump.rdb。但是要把文件修改坏了并且保存的话,首选要知道redis进程的进程id。
再通过kill进程的方式,将进程的直接杀死。在这里虽然改坏了,但是看起来好像没什么问题,但是实际上有无问题得看实际情况。
文件格式检查工具:redis-check.rdb。
RDB文件的特点:
- RDB是一个紧凑的文件,适合全量备份的场景,比如每六个小时执行bgsave的场景,并且把RDB文件复制到远程机器或者文件系统中。
- Redis加载RDB回复数据的方式远远快于AOF的方式。(使用二进制进行组织数据)。
- RDB方式数据没办法做到实时持久化,因为每次bgsave运行都要执行fork创建子进程,属于重量级操作,频繁执行成本过高。
- Rdb文件用二进制的格式保存,Redis版本更替有多个版本,兼容性可能有风险。新老版本的RDB文件可能不同。
AOF(Append only file)持久策略:
RDB最大的问题就是不能实时持久化保存数据,两次快照期间,实时的数据可能随着重启而丢失。
原理:
类似于mysql的binlog,并不是存储数据,而是存储了该操作,存储在文件中,将操作内容存储在文件中,当redis重新启动的时候,就会读取AOF中的内容用来恢复数据。启动AOF,RDB就不生效了,AOF一般是关闭状态!!!!!!!
要将该命令改为yes,就能生效,就可以启动AOF了。
此时将AOF的操作保存在"appendonly.aof"文件之下,也存在/var/lib/redis的文件下(redis.conf下)。AOF是一个文本文件,每次进行操作都会被记录到文本文件中。
AOF是否会影响性能:
引入AOF后又要写内存也要写硬盘,并没有因为这样影响到redis处理请求的速度。
- AOF机制并非是直接把线程数据写入硬盘,而是在内存缓冲区中,积累一波后,写入硬盘。大大降低了写入硬盘的次数。
- 硬盘顺序读写相对随机读写的速度快,AOF每次是把数据写到文件的末尾,属于顺序写入。
如果在缓冲区中的数据,没来得及写入硬盘,就会丢失,就会导致数据不可靠!
刷新频率实际上是可以程序员自己设置的,刷新频率比较高,性能影响大,同时数据可靠性就比较高。
默认的策略是everysec每秒进行刷新,最多也就损失一秒的数据,可以在etc/redis下的redis.conf文件查看刷新策略。
AOF的文件大小会影响到redis下一次的启动时间,reids重新启动的时候要读取AOF文件的内容,而且有一些AOF文件是冗余的。比如分别多次插入数据,但是可以一次性插入成功,却分了很多次,就会造成冗余。
Redis的重写机制:
上述我们讲了redis会因为一些原因,记录冗余的AOF数据,所以我们的redis就有重写机制,可以去除掉这些冗余的数据,保留最后的结果,做到给AOF瘦身的这样的效果。
手动触发重写机制:
在redis中直接调用bgrewriteaof命令,就可以触发重写机制了。
自动触发重写机制:
AUTO-AOF-REWIRITE-MIN-SIZE:触发重写的AOF最小文件大小,默认大小是64MB。
AUTO-AOF-REWRITE-PERCENTAGE:表示需要重写时文件相对上一次增长的比例,比如原本AOF文件是1G,下次触发重写的时候就是1.5G的时候触发重写机制。
重写流程:
重写时候并不关心原本AOF文件中有啥,而是内存中的内存状态,同样的重写的时候,也会创建出子进程,子进程会读取内存中的数据,并且生成新的AOF文件,对他进行重写,也就是观察最新的内存数据,然后重写到AOF文件中。内存中的结果就已经是,用户把AOF重写后的状态。
此处写进程的方式就是很类似于RDB文件的镜像快照,不过RDB是二进制,这里是文本文件。在这里fork之前的的修改是存储在旧的AOF文件中,而fork之后则是存储在新的AOF文件中。子进程这边AOF写完后,会通知父进程,父进程会把aof-write-buf中的数据(fork之后的数据)也写入新的AOF文件中。但是这时候还在写旧的AOF文件。
如果在执行重写操作的时候,又来一个重写命令,此时不会执行命令,就直接返回了。如果在bgrewriteaof的时候正在生,成rdb快照,就会等待rdb快照生产完毕,再进行aof重写。
旧的AOF和新的AOF文件:
旧的AOF文件不能不写,如果考虑到极端情况,主机断电了,就会导致新的AOF文件不完整,旧的已经丢失了,导致文件不完整。旧的AOF文件在更替的过程中仍然会进行写入,防止主机断电导致新的没有及时更替,新的文件通过子进程和auto-rewrite-buf进行写入,子进程是在fork之前进行写入,而aof-rewrite-buf是在fork之后将数据写入新的aof文件中。
这里多次设置了key值,然而key3的值实际上最后只有555,其实最后只要设置555就可以了。
此时通过手动触发bgrewriteaof的方法,就能够重写AOF文件,变成RDB(在/var/lib/redis下边)。
混合持久化:
redis引入了混合持久化的特点,结合了RDB和AOF的特点,按照AOF的方式每个请求/操作,都记录到文件中,但是触发AOF重写的操作,就会将当前内容转化为二进制(也就是RDB文件),后续再进行操作,就追加到文件后面ÿ