文章目录
- 持久化篇
- 1、AOF持久化是怎么实现的?
- 1.1、AOF日志
- 1.2、三种写回策略
- 1.3、AOF重写机制
- 1.4、AOF后台重写
- 2、RDB快照是怎么实现的?
- 2.1、快照怎么使用
- 2.2、执行快照时,数据能被修改吗?
- 2.3、RDB和AOF合体
- 3、Redis大key对持久化有什么影响?
- 3.1、大Key对AOF日志的影响
- 3.2、大Key对AOF重写和RDB的影响
持久化篇
1、AOF持久化是怎么实现的?
1.1、AOF日志
保存写操作到日志的持久化方式,就是Redis里的AOF持久化功能,注意只会记录写操作,读操作命令是不会被修改的,因为没有意义。=> 需要修改redis.comf里的参数:appendonly yes
&& appendfilename “appendonly.aof”
分别表示是否开启AOF持久化,AOF持久化文件的名称。
AOF文件格式 *3代表三部分 $3代表命令的字节数位3字节
而且Redis是先执行写操作,再记录命令到日志,这样有两个好处:
1)避免额外的开销,如果写操作语法有问题,记录到AOF日志,恢复的时候可能会出问题。
2)不会阻塞当前写操作命令的执行
风险:
1)redis还没来得及将命令写入到硬盘,服务器宕机,数据就会有丢失的风险
2)写操作命令执行成功后才记录AOF体制,所以不会阻塞当前命令的执行,但是可能给下一个命令
带来阻塞风险,因为写入内存和写入磁盘的命令都是主进程完成的
1.2、三种写回策略
用户态(执行写操作命令 -> 命令追加到server.aof_buf)—— I/O系统调用write —— 内核态(内核缓冲区page cache -> 由内核发起写操作 -> 硬盘)
具体实现步骤:
1、Redis执行完写操作后,会将命令追加到server.aof_buf缓冲区
2、通过write()系统调用,将aof_buf缓冲区的数据写入AOF文件,此时数据没有写入到磁盘,而是拷贝到内核缓冲区page cache,等待内核将数据写入硬盘;
3、具体内核缓冲区的数据什么时候写入到硬盘,由内核决定。
写回磁盘策略 => redis.conf配置文件中的appendsync三种参数可以填
1、Always:总是,每次执行完写命令操作后,同步将AOF日志数据写回磁盘
2、Everysec:每秒,每次执行完写操作后,现将命令写入到AOF文件到内核缓冲区,每隔一秒将缓冲区的内容写回磁盘
3、NO:交给操作系统,命令写入AOF内核缓冲区,由操作系统决定什么时候写回磁盘
这三种写回策略其实就是什么时机执行fsync()函数,Always每次写入就执行;Everysec创建一个异步任务来执行;No就是永不执行。
1.3、AOF重写机制
AOF日志是一个文件,随着写操作命令越来越多,文件的大小会越来越大 => 为了避免AOF文件越写越大,提供了AOF重写机制,文件大小超过设定的阈值之后,Redis会启动AOF重写机制来压缩AOF文件。
具体的做法是:读取当前数据库中的所有键值对,将每一个键值对用一条命令记录到新的AOF文件中,等到全部记录完之后,将新的文件替换到现在的AOF文件。相当于记录对某个键的最新值,删除掉旧值,减少了AOF日志的大小
,所以重写需要重写到新的AOF文件中,失败的话,直接删除这个文件就好,不会对现有的AOF文件造成影响。
1.4、AOF后台重写
重写的时候,这个过程是比较耗时的,所以重写的操作不能放在主进程里 => 重写AOF过程是由后台子进程bgwrriteaof来完成的
,有两个好处:
1)子进程重写AOF期间,主进程可以继续处理命令请求,从而避免阻塞主进程;
2)子进程带有主进程的数据副本,注意不是线程,线程共享内存,会在修改共享内存数据的时候加锁,影响性能,而父子进程共享内存数据,以只读的方式,当父子进程任意一方修改了该共享内存,就会发生写时复制,于是父子进程就有了独立的数据副本,不需要加锁也可以保证数据安全。
实现过程
1、主进程fork()系统调用生成bgrewriteaof子进程,页表复制给子进程(虚拟地址和物理地址的映射关系),共享物理内存,内存的权限为可读
2、当父进程或子进程向这个内存发起写操作时,CPU就会触发写保护中断
,进行物理内存的复制,重新设置映射关系,父子进程的内存读写权限设置为可读写
,最后才对内存进行写操作 => 写时复制
3、存在两个阶段导致父进程阻塞:1)创建子进程,复制页面,页面太大,阻塞时间长;2)子进程或父进程修改共享数据,发生写时复制,拷贝物理内存,阻塞时间长。
4、可能出现的问题:
1)在写时复制阶段,修改的是一个bigkey(数据量比较大的key-value),这个时候复制的物理内存数据的过程会比较耗时,有阻塞主进程的风险。
2)主进程修改key-value,但是在子进程内存数据跟主进程数据不一致 => 设置AOF重写缓冲区,这个缓冲区在创建bgrewriteaof子进程就开始使用
2)当子进程完成AOF重写工作的时候,发送信号给主进程 => AOF重写缓冲区的所有内容追加到新的AOF内容中,使得新旧AOF文件的数据库状态保持一致;将新的AOF文件进行改名,覆盖现有的AOF文件。
2、RDB快照是怎么实现的?
AOF文件的内容是操作命令(不是实际数据)、RDB文件的内容是二进制数据(某一个时刻的数据库实际数据);RDB恢复数据的效率比AOF高些,因为RDB文件读入内存就行,而AOF需要额外的执行操作命令步骤才能恢复数据。
2.1、快照怎么使用
1)save 命令,在主线程生成RDB文件,由于和执行操作命令在同一线程,所以如果写入RDB文件的时间太长,会阻塞主进程。
2)bgsave 命令,会创建一个子进程来生成RDB文件,这样避免主线程阻塞。
=> 可以配置每隔一段时间自动执行一次bgsave命令
save 900 1 // 900秒内,对数据库至少进行一次修改
save 300 10 // 300秒内,对数据库至少进行10次修改
save 60 10000 // 60秒内,对数据库至少进行10000次修改
2.2、执行快照时,数据能被修改吗?
可以的! => 关键技术:写时复制
1)当主线程(父进程)对这些共享的数据只进行读操作的时候,是互不干扰的
2)当主线程执行写操作的时候,就会发生写时复制,该数据块的物理内存就会被复制一份,然后主线程在这个数据副本进行修改操作,bgsave会在原来的数据写入到RDB文件。
所以这样意味着子进程写入到RDB文件的数据只能是原本的内存数据,修改的部分交由下一次bgsave快照。
极端情况:刚fork时,途中主进程处理了写操作,修改了共享内存,修改的数据的内存会被复制一份 => 极端情况下,内存的占用时原来的两倍。
2.3、RDB和AOF合体
RDB尽管数据恢复速度快,但是频率不好把握。
1)频率太低,两次快照间一旦服务器发生宕机,就可能丢失较多数据;
2)频率太高,频繁的写入磁盘和创建子进程会带来额外的性能开销。
=> 混合使用AOF日志和内存快照,亦称混合持久化 aof-use-rdb-preamble yes
,工作在AOF日志重写过程
具体的做法是:(前半部分是RDB格式的全量数据 + 后半部分AOF格式的增量数据)
1)AOF重写日志的时候,fork出来的重写子进程会先将与主线程共享的内存数据以RDB方式写入到AOF文件;
2)然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以AOF方式写到AOF文件;
3)写入完成后,将含有RDB格式和AOF格式的文件 替换 旧的AOF文件
3、Redis大key对持久化有什么影响?
3.1、大Key对AOF日志的影响
1、三种写回策略,在持久化大Key的时候,会有什么影响?
- Always策略的时候,如果写入的是一个大Key,主线程在执行fsync()函数的时候,阻塞的时间会比较久,因为当写入数据量很大的时候,数据同步到硬盘这个过程是很耗时的。
- Everysec策略,由于是异步执行fsync()函数,所以大key持久化过程不会影响到主线程。
- No策略,永不执行fsync(), 所以大key持久化过程不会影响到主线程。
3.2、大Key对AOF重写和RDB的影响
当AOF日志写入很多大Key,AOF日志文件的大小会很大,很快就会触发AOF重写机制。
AOF重写机制和RDB快照都会分别通过fork()函数来创建一个子进程来处理任务。
=> info命令可以获取到latest_fork_usec
指标,表示Redis最近一次fork操作的耗时,如果fork耗时很大,如超过1s需要做出调整
1)单个实例的内存占用在10GB以下,fork很快就能返回
2)Redis单纯做缓存,不关心Redis数据安全性问题,直接关闭AOF重写
3)在主从架构,适当的调大repl-backlog-size,避免主节点频繁的使用全量同步的方式。
1、什么时候发生物理内存的复制?
1)创建子进程途中,要复制父进程页表等数据结构
2)当父进程或子进程向共享内存发起写操作,CPU就会触发写保护中断,写时复制
2、linux开启了内存大页,会影响redis性能
3、删除大key
使用unlink命令(异步不阻塞),而不是用delete(会阻塞主进程)