<Redis>RDB持久化

前言:

Redis是一个键值对数据库服务器,因为它是内存数据库,它将数据都保存在内存里面,所以如果不想办法进行持久化,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。

为了解决这个问题,Redis提供了RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里面,避免数据意外丢失。

Redis持久化既可以手动执行,也可以根据配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中,RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态。

从内存生成RDB文件到磁盘的主要函数是rdbSave

从磁盘还原数据库状态到内存的主要函数是rdbLoad

工作原理

·Redis调用fork(fork操作会阻塞),产生一个子进程

·子进程把数据写到一个临时的RDB文件

·当子进程写完新的RDB文件后,把旧的RDB文件替换掉。

SAVE&BGSAVE

有两个Redis命令可以用于生成RDB文件,一个是SAVE,一个是BGSAVE。

SAVE命令会阻塞Redis服务器进程,SAVE的过程不能处理任何命令请求,直到RDB文件创建完毕为止。

BGSAVE命令会fork出一个子进程,子进程负责创建RDB文件,父进程仍然可以处理命令请求。

创建RDB文件的实际工作由rdb.c/rdbSave函数完成,SAVE和BGSAVE命令会以不用的方式调用这个函数,下面用一段伪代码展示它们调用的区别:

def SAVE():

#创建RDB文件

rdbSave()


def BGSAVE():

#创建子进程

pid = fork()

if(pid==0):

       #子进程负责创建RDB文件

       rdbSave()

       #完成以后向父进程发送信号

       signal_parent()

elif pid > 0:

       #父进程继续处理命令请求,并通过轮询等待子进程的信号

       handle_request_and_wait_signal()

else:

       #处理出错情况

       handle_fork_error()


和使用SAVE或者BGSAVE命令创建RDB文件不同,RDB文件的载入工作是在服务器启动的时候自动执行的,所以Redis没有专门用于载入RDB文件的命令,只要Redis服务器在启动时检测到RDB文件的存在,它就会自动载入RDB文件。

另外,因为AOF文件的更新频率比RDB文件的更新频率高,所以:

·如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库状态。

·只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态。

载入RDB文件的实际工作由rdb.c/rdbLoad函数完成,这个函数和rdbSave函数之间的关系就是逆向的。


SAVE命令执行时的服务器状态

前面提到过,当SAVE命令执行时,Redis服务器会被阻塞,所以当SAVE命令正在执行时,客户端发送的所有命令请求都会被阻塞,只有在服务器执行完SAVE命令、重新开始接受命令请求之后,客户端发送的命令才会被处理。

BGSAVE命令执行时的服务器状态

因为BGSAVE的保存工作是由子进程执行的,所以在子进程创建RDB文件的过程中,Redis服务器仍然可以继续处理客户端的命令请求,但是,在BGSAVE命令执行期间,服务器处理SAVE、BGSAVE、BGREWRITEAOF三个命令的方式会和平时有所不同。

首先,在BGSAVE命令执行期间,客户端发送的SAVE命令会被拒绝,服务器禁止SAVE 和 BGSAVE同时执行是为了避免父进程和子进程同时执行两个rdbSave调用,防止产生竞争条件。

其次,在BGSAVE命令执行期间,客户端发送的BGSAVE命令会被服务器拒绝,因为同时执行两个BGSAVE也会产生竞争条件。

最后,BGREWRITEAOF和BGSAVE两个命令不能同时执行:

·如果BGSAVE命令正在执行,那么客户端发送的BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行。

·如果BGREWRITEAOF命令正在执行,那么客户端发送的BGSAVE命令会被服务器拒绝。

BGREWRITEAOF和BGSAVE都是由子进程完成的,虽然没有什么冲突的地方,主要是出于性能的考虑,并发两个子进程同时执行大量的磁盘写入操作,不是什么好主意。


RDB文件载入期间的服务器状态

RDB文件载入期间Redis服务器处于阻塞状态,直到载入工作完成。


自动间隔性保存

用户可以通过save选项设置多个保存条件,只要其中任何一个条件被满足,服务器就会执行BGSAVE命令。

举个例子:

如果我们向服务器提供以下配置:

save 900 1

save 300 10

save 60 10000

那么只要服务器满足以下三个条件中的任意一个,BGSAVE命令就会被执行。

·服务器在900秒以内,对数据库进行了至少1次修改。

·服务器在300秒以内,对数据库进行了至少10次修改。

·服务器在60秒以内,对数据库进行了至少10000次修改。

这也是redis服务器的默认保存条件,如果用户进行了自定义的配置,将会采用用户的配置。


自动保存的实现

服务器程序会根据save选项的配置,设置服务器状态redisServer结构的saveParams属性。

struct saveParams{

 // 秒数

 time_t  seconds;

// 修改数 

int changes;

}


dirty计数器和lastsave属性

除了saveparams数组之外,服务器状态还维持着一个dirty计数器,以及一个lastsave属性。

·dirty计数器记录距离上一次成功执行SAVE或者BGSAVE命令之后,服务器对数据库状态进行了多少次修改

·lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE或者BGSAVE命令的时间。

struct redisServer{

  // ....

   // 修改计数器

  long long dirty;

  // 上一次保存的时间

  time_t lastsave;

}

检查保存条件是否满足

Redis的服务器周期性操作函数serverCron默认每隔100毫秒就会执行一次,该函数用于对正在运行的服务器进行维护,它的其中一项工作就是检查save选项所设置的保存条件是否已经满足,如果满足的话,就执行BGSAVE命令。

以下伪代码展示了serverCron函数检查保存条件的过程:

def  serverCron():

     #...

    # 遍历所有保存条件

    for saveparam in server.saveparams:

              save_interval = unixtime_not() - server.lastsave

              // 如果修改次数超过条件且时间超过条件,就执行BGSAVE命令

              if  server.dirty >= saveparam.changes and \

                  save_interval > saveparam.seconds:

                   BGSAVE()

#....


重点回顾

·RDB文件用于保存和还原Redis服务器所有数据库中的所有键值对数据。

·SAVE命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器。

·BGSAVE命令由子进程执行保存操作,所以该命令不会阻塞服务器。

·服务器状态中会保存所有用save选项设置的保存条件,满足任意一个条件服务器就会自动执行BGSAVE命令。

·RDB文件是一个经过压缩的二进制文件,由多个部分组成。

·对于不同类型的键值对,RDB文件会使用不同的方式来保存他们。

·linux操作系统在fork子进程的时候使用“写时复制”技术,由于fork的过程父进程有写入操作,就会触发父子进程的复制操作,复制操作的效率跟父进程的内存大小挂钩,而且会阻塞。所以redis内存越大,fork阻塞的时间越长。

RDB的缺点:

如果你希望在Redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB 不适合你。

虽然你可以配置不同的Save 时间点(例如每隔5 分钟并且对数据集有100 个写的操作),但是Redis要完整的保存整个数据集是一个比较繁重的工作。你通常会每隔5 分钟或者更久做一次完整的保存,万一Redis意外宕机,你可能会丢失几分钟的数据。

·RDB需要经常 Fork子进程来保存数据集到硬盘上,当数据集比较大的时,Fork的过程是非常耗时的,可能会导致 Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU 性能不是很好的情况下,这种情况会持续1 秒,AOF也需要 Fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。


参考资料:

Scipathi Krishnan <Redis RDB 文件格式>

Scipathi Krishnan <Redis RDB 版本历史>

Redis作者博文<Redis persistence demystified>

黄健宏 <Redis设计与实现>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • 从这篇文章开始,将依次介绍Redis高可用相关的知识——持久化、复制(及读写分离)、哨兵、以及集群。 本文将先说明...
    不变甄心阅读 693评论 0 4
  • 前言 在上一篇文章中,介绍了Redis内存模型,从这篇文章开始,将依次介绍Redis高可用相关的知识——持久化、复...
    Java架构阅读 2,308评论 3 21
  • 一、Redis高可用概述 在介绍Redis高可用之前,先说明一下在Redis的语境中高可用的含义。 我们知道,在w...
    空语阅读 1,596评论 0 2
  • Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消...
    杰哥长得帅阅读 7,639评论 0 2
  • RDB持久化 Redis是内存数据库,数据库状态都在内存里边,需要RDB持久化功能将内存中的数据库状态保存到磁盘里...
    涵仔睡觉阅读 288评论 0 1