目录
前言
redis的持久化可能要配合mysql的redolog 一起看理解,还有binlog。
都差不多的玩意~
回头补一下es的实现~
做事情要学会类比,学习工具里面的思想,看看能不能用到业务中去~
做一个高逼格的人 嘿嘿
猜想
如果看到过别的持久化,不管是mysql也好,还是什么mq也好,持久化目的是什么?
我觉得是
1.防止宕机之后数据丢失,保证数据的准确性;
2.宕机可以恢复;
3.类似kafka的话,宕机之后可以从offset恢复,而且有多partition机制
类似mysql,有redolog和binlog双重保证~宕机可以恢复binlog点位,然后进行回放~
4. 有一种思想就是,我们先写log,后写数据,两阶段提交类似mysql,这样的话就不会有log写了,但是内存或者数据查不到的问题~(但是redis其实是先执行指令,再进行日志存盘的,跟别的存储引擎不太一样~)
以上是最近在复习这方面知识的一个感受~
因为以前都看过redis持久化,知道aof和rdb,所以下面就直接开始记录~
快照
RDB是一种快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件的数据到内存当中恢复数据。
开启RDB持久化方式很简单,客户端可以通过向Redis服务器发送save或bgsave命令让服务器生成rdb文件,或者通过服务器配置文件指定触发RDB条件。(save是同步操作,会阻塞线上不要用哇)
注意:
快照是全量备份,AOF是增量备份;
快照是二进制序列化形式,存储紧凑,而aof日志记录是内存数据修改的指令记录文本;
(aof简直就是redolog的一毛一样的感觉)
需要定期进行aof重写,给aof日志进行瘦身;
快照的痛苦的点
1.redis单线程,线程需要同时负责多个客户端套接字的并发读写操作,和内存数据结构的逻辑读写;
2.服务器的请求同时,redis还要进行内存快照;内存快照要求redis 必须进行io操作,但是文件io操作不能用多路复用api;
3.单线程还要进行io,会严重影响性能,所以需要一遍持久化买一遍响应客户端
redis怎么做快照
使用操作系统的多进程cow (copy on write)
(所以说知识真是互通的)
cow简单描述:写的时候复制,直接映射原始盘的内容;
当原始盘旧数据有修改,将修改前的旧数据存入前端盘
对前端盘修改不会写到原始盘
一头雾水是不是,往下看
快照cow
Copy On Write 机制
核心思路:fork一个子进程,只有在父进程发生写操作修改内存数据时,才会真正去分配内存空间,并复制内存数据,而且也只是复制被修改的内存页中的数据,并不是全部内存数据;
- Redis中执行BGSAVE命令生成RDB文件时,本质就是调用Linux中的fork()命令,Linux下的fork()系统调用实现了copy-on-write写时复制;
- fork()是类Unix操作系统上创建线程的主要方法,fork用于创建子进程(等同于当前进程的副本);
- 传统的普通进程复制,会直接将父进程的数据拷贝到子进程中,拷贝完成后,父进程和子进程之间的数据段和堆栈是相互独立的;
- copy-on-write技术,在fork出子进程后,与父进程共享内存空间,两者只是虚拟空间不同,但是其对应的物理空间是同一个;
(这个cow机制 跟kafka的零拷贝好像啊!!其实也不太像,就是都是节省拷贝的过程节省资源)
总结:copy-on-write技术,在fork出子进程后,与父进程共享内存空间,两者只是虚拟空间不同,但是其对应的物理空间是同一个,那我们就不用多冗余一份数据了
Linux中CopyOnWrite实现原理
fork()之后,kernel把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。
当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel的一个中断例程。中断例程中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。
(!!!所以这才是为啥叫做写时复制的原因,也就是我们如果是只读的话,我们就不复制,默认readonly,但是一旦有了更改(指的是主线程),那我们就把更改的内容复制一部分,修改的内容单独分离一个页面, 因为父子线程是readonly的, 发生了异常中断, 主线程去处理要更改的内容就好了,子线程看到的就是快照!
这样也能保证redis的子进程复制的正确~)
CopyOnWrite的好处
1、减少分配和复制资源时带来的瞬时延迟;
2、减少不必要的资源分配;
CopyOnWrite的缺点:
如果父子进程都需要进行大量的写操作,会产生大量的分页错误(页异常中断page-fault);
Redis中的CopyOnWrite
Redis在持久化时,如果是采用BGSAVE命令或者BGREWRITEAOF的方式,那Redis会fork出一个子进程来读取数据,从而写到磁盘中。
总体来看,Redis还是读操作比较多。如果子进程存在期间,发生了大量的写操作,那可能就会出现很多的分页错误(页异常中断page-fault),这样就得耗费不少性能在复制上。
而在rehash阶段上,写操作是无法避免的。所以Redis在fork出子进程之后,将负载因子阈值提高,尽量减少写操作,避免不必要的内存写入操作,最大限度地节约内存。
(因为redis是读操作比较多~rehash 写操作是必须的因为迁移了一个新的hash结构~ )
redis的快照的疑问
RDB过程中会fork一个子进程,子进程做数据备份操作,主进程继续对外提供服务,所有Redis服务不会阻塞;
Copy On Write 机制,备份的是开始那个时刻内存中的数据;准确的将,是bgsave命令执行的时候的快照,那一个时刻的。
从那一个时刻起,父子线程看到的内存内容就是一样的,从上面分析来看就是readonly的,如果主线程有请求进来要改数据了,我们就让主线程单独分离一个页面出来就好,等rdb操作完成(也许弄回去,这个我没有注意也没有看,再和回去,或者都是内存的地址也许就这样放着了~)
Copy On Write 机制不需要把整个内存的数据都复制一份,因为是写时复制
RDB的几个优点
-
与AOF方式相比,通过rdb文件恢复数据比较快。
-
rdb文件非常紧凑,适合于数据备份。
-
通过RDB进行数据备,由于使用子进程生成,所以对Redis服务器性能影响较小。
-
copyonwrite只是一个寻址的过程,纳秒级别的。而aof每次都是写盘操作,毫米级别。
rdb的缺点
-
使用bgsave命令在forks子进程时,如果数据量太大,forks的过程也会发生阻塞,另外,forks子进程会耗费内存。(耗费内存,也不是一定不会发生阻塞的)
-
save同步肯定不能用
-
需要配合aof一起用,因为rdb每次全量,太多了,容易丢失很多数据。
redis的内存页大小
一般每个页面的大小只有4kb,redis实例有成千上万个页面~
rbd因为redis读多写少,所以cow时候的复制操作并不会很多、
复制过程,越来越多的共享页面被分离出来,内存会持续增长,但是也不会超过现有数据内存的2倍大小。
AOF
AOF是增量备份;aof日志记录是内存数据修改的指令记录文本;
概念
aof是redis的顺序指令序列,aof日志只记录对内存进行修改的指令记录,
redis是先讲内存中的东西改了,执行指令之后,才进行日志存盘。这个不同于别的数据结构 比如mysql hbase
bgrewriteaof
这个指令可以进行瘦身,开辟一个子进程,然后对内存进行遍历,然后转换成一系列的redis 的操作指令,序列化到一个新的aof日志文件中。
异常宕机
因为aof日志是稳健性存在,但是程序对aof日志文件进行写操作,其实是写到 (内存缓存中),然后内核会一步的将脏数据刷会磁盘中。
如果突然宕机,aof可能没有来得及刷到磁盘,这时候日志丢失怎么办?
linux 的glibc提供的fsync 函数可以将指定的文件内容强制从内核缓存刷到磁盘。
只要redis进程实时调用这个函数就可以保证aof日志不丢失,但是fsync是一个磁盘io操作,很慢的;
所以生产服务器,redis通常是每隔一秒左右执行一次刷回磁盘,做一个折中。
(总结,aof先写道内存缓存中,之后才弄到磁盘上,防止数据丢失,应该合理的配置这个刷回的时间~)
定期的aof重写
需要定期进行aof重写,给aof日志进行瘦身;
(因为可能会有很多重复指令,需要进行瘦身的)
执行一个 AOF文件重写操作,重写会创建一个当前 AOF 文件的体积优化版本。
即使 BGREWRITEAOF 执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在 BGREWRITEAOF 成功之前不会被修改。
从 Redis 2.4 开始,AOF 重写由 Redis 自行触发, BGREWRITEAOF 仅仅用于手动触发重写操作。但网上有网友说已经3.2.5版本了,貌似redis还是没有自动触发BGREWRITEAOF
一般只会在从节点进行同步备份
因为主节点,遍历全部内存,会很加大系统的负担,aof的fsync是个耗时的io操作,降低性能的。
怎么进行aof瘦身?
使用rewirte机制,rewrite机制现在达到一定的条件redis会自动触发
其具体的流程就是:
1,redis主进程fork一个子进程
2,子进程根据当前内存的数据,构建一个新的日志,写入一个新的AOF文件中 (有点类似于,我把内存中有的数据,写到aof中~)
3,这段时间内,redis接收到的client的修改操作,都会在内存中新起一个日志文件去进入,并同步到旧的AOF文件中 (两个都接受者)
4,当子进程完成了任务,redis就会把新的日志文件追加到新的AOF文件中
5,使用新的AOF文件替换旧的AOF文件
总结
快照是父进程fork时的快照,触发bgsave父进程还会进行一次判断的,如果当前存在正在执行的RDB和AOF会直接返回的,如果不存在,那么父进程会阻塞,fork出子进程,因为fork的COW机制,这个子进程会有父进程fork时的内存快照。
redis是先将内存中的东西改了,执行指令之后,才进行日志存盘。这个不同于别的数据结构 比如mysql hbase
aof先写道内存缓存中,之后才弄到磁盘上,防止数据丢失,应该合理的配置这个刷回的时间~
通常线上服务是混合持久化,这样的话,把aof和rdb放到一起,不会丢失很多数据,而且重启的效率也会很高
参考:
https://www.cnblogs.com/kgdxpr/p/7155718.html
https://blog.csdn.net/lh87270202/article/details/106430154
《redis深度历险》-钱文忠