3.5写时复制
Redis在使用RDB方式进行持久化时,会用到写时复制机制。写时复制的效果: bgsave子进程相当于复制了原始数据,而主线程仍然可以修改原来的数据。
对Redis来说,主线程fork出bgsave子进程后,bgsave子进程实际是复制了主线程的页表。这些页表中,就保存了在执行bgsave命令时,主线程的所有数据块在内存中的物理地址。这样一来,bgsave子进程生成RDB时,就可以根据页表读取这些数据,再写入磁盘中。如果此时,主线程接收到了新写或修改操作,那么,主线程会使用写时复制机制。具体来说,写时复制就是指,主线程在有写操作时,才会把这个新写或修改后的数据写入到一个新的物理地址中,并修改自己的页表映射。
3.5.1写时复制底层原理
bgsave子进程复制主线程的页表以后,假如主线程需要修改虚页7里的数据,那么,主线程就需要新分配一个物理页(假设是物理页53),然后把修改后的虚页7里的数据写到物理页53上,而虚页7里原来的数据仍然保存在物理页33上。这个时候,虚页7到物理页33的映射关系,仍然保留在bgsave子进程中。所以,bgsave子进程可以无误地把虚页7的原始数据写入RDB文件。
3.6每秒做一次RDB快照?
快照时间越短,即使某一刻发生了宕机,上一刻的数据做了快照,损失的数据也不会太多。但是快照的间隔时间就很关键了。
在T0时刻做了一次快照,在T0+t的时刻发生了宕机,进行数据恢复成T0那么5和9的数据就无法进行恢复了
是不是可以每秒做一次快照?毕竟,每次快照都是由bgsave子进程在后台执行,也不会阻塞主线程。
解释:
这种想法其实是错误的。虽然bgsave执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销。
一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
另一方面,bgsave子进程需要通过fork操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁fork出bgsave子进程,这就会频繁阻塞主线程了。那么,有什么其他好方法吗?
优缺点
RDB快照快速恢复数据,但是生成RDB文件频率设置的低的话就可能会出现,丢失数据过多,生成RDB文件过快的话,又会增加额外的开销。
频繁写入开销问题:
频繁生成RDB文件写入磁盘,会造成磁盘压力过大,会出现上一个RDB还没有处理完,下一个RDB文件写入就已经开始了,造成死循环。
bgsave会调用glibc中,fork函数生成子进程,子进程会阻塞主线程,主线程内存越大,阻塞时间越长。子线程的创建本身是对主线程有阻塞的。
3.7RDB增量快照
增量快照,所谓增量快照说的就是,做完一次快照之后,后续的快照只对修改的数据进行快照,这样可以避免每次全量快照的开销。
比如对一万条数据进行了修改,你只需要记录这一万条修改数据的信息,就需要有1万条额外的记录,进入额外的空间开销比较大。