redis-持久化

redis的持久化有两种方式:RDB和AOF。

  • RDB持久化功能是将某个时间点的数据库状态保存到一个RDB文件上(通过保存数据库的键值对来记录数据库状态)。RDB持久化功能所生成的RDB文件是一个经过压缩的二进制文件,通过该文件可以还原成生成RDB文件时的数据库状态。
  • AOF持久化是通过保存redis服务器所执行的写命令来记录数据库状态的。

1.RDB的创建与载入

RDB文件的生成有两个命令,SAVE和BGSAVE。save会直接阻塞服务器进程,bgsave是派生出一个子进程,然后父进程继续处理命令请求。

RDB文件的载入工作是在服务器启动时自动执行的,所以没有专门用于载入rdb文件的命令,只要redis服务器在启动时检测到rdb文件存在,它就会自动载入rdb文件。

如果服务器开启了AOF持久化功能,那么服务器优先使用aof文件来还原数据库状态。只有在aof持久化功能处于关闭状态时,服务器才会使用rdb文件来还原数据库状态。(流程为:服务器启动->执行载入程序->是否开启aof持久化功能?是,载入aof文件;否,载入rdb文件)。

1.1 bgsave命令执行时的服务器状态

1.在执行BGSAVE命令期间,客户端发送的SAVE和BGSAVE都会被服务器拒绝。因为这两个命令同时执行会产生竞争条件。

2.BGSAVE命令正在执行,那么客户端发送的BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕后执行。如果BGREWRITEAOF命令正在执行,那么客户端发送的BGSAVE命令会被服务器拒绝。这两个命令都由子进程来执行,并没有冲突,不能同时执行他们是一个性能方面的考虑,并发两个子进程,且都是同时执行大量的磁盘写入操作,不是一个好主意。

1.2 RDB文件载入时的服务器状态

服务器在载入RDB文件时,会一直阻塞,直到载入完成。

2.自动间隔性保存RDB

用户可以通过服务器的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。

struct redisServer
{
    //...

    // 记录了保存条件的数组
    struct saveparam *saveparams;

    // 修改计数器
    long long dirty;

    //上一次执行保存的时间
    time_t lastsave;

    //...   
};

当保存条件有一个被满足时,服务器就会执行BGSAVE命令。

  • dirty计数器记录距离上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改。
  • lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间。

例如SADD database redis mongodb mariadb,那么程序会将dirty计数器的值增加3.

2.1 检查保存条件是否满足

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

3. AOF的实现

AOF持久化功能的实现可以分为命令追加(append),文件写入,文件同步(sync)三个步骤。

3.1 命令追加

当AOF持久化功能处于打开状态时,服务器在执行完一个写命令之后,会以redis的命令请求协议的格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾。

struct redisServer
{
    // ...

    //aof缓冲区
    sds aof_buf;

    // ...
};

3.2 AOF文件的写入与同步

redis的服务器进程就是一个事件循环(loop),这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像serverCron函数这样需要定时运行的函数。

事件循环流程如下:

def eventLoop():
    while True:

        # 处理文件事件,接收命令请求以及发送命令回复
        # 处理命令请求时可能会有新内容被追加到aof_buf缓冲区中
        processFileEvents()

        # 处理时间事件
        processTimeEvents()

        # 考虑是否要将aof_buf中的内容写入和保存到AOF文件里面
        flushAppendOnlyFile()

flushAppendOnlyFile函数的行为由服务器配置的appendfsync选项的值来决定。默认值为everysec.

文件的写入和同步
为了提高文件的写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时保存在一个内存缓冲区里面,等到缓冲区的空间被填满,或者超过了指定的时限后,才真正的将缓冲区中的数据写入到磁盘里面。
这种做法虽然提高了效率,但也为写入数据带来了安全问题,因为如果计算机发生停机,那么保存在内存缓冲区里面的写入数据将会丢失。
为此,系统提供了fsync和fdatasync两个同步函数,它们可以强制让操作系统立即将缓冲区里的数据写入到硬盘里面,从而确保写入数据的安全性。

AOF持久化的效率和安全性
服务器配置appendfsync选项的值直接决定AOF持久化功能的效率和安全性。

  • 当appendfsync的值为always时,服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,并且同步AOF文件,所以always的效率是三个选项中最低的一个,但从安全性来讲,always是最安全的,因为即使出现故障停机,aof持久化也只会丢失一个事件循环中所产生的命令数据。
  • 当appendfsync的值为everysec时,服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,并且每隔一秒就要在子线程中对AOF文件进行一次同步。从效率上讲,everysec模式足够快,并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。
  • 当appendfsync的值为no时,服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件,至于何时对AOF文件进行同步,则由操作系统控制。因为处于no模式下的flushAppendOnlyFile调用无须执行同步操作,所以该模式下的AOF文件写入速度总是最快的,不过因为这种模式会在系统缓存中积累一段时间的写入数据,所以该模式的单次同步时长通常是三种模式中时间最长的。从平摊操作的角度来看,no模式和everysec模式的效率类似,当出现故障停机时,使用no模式的服务器将会丢失上次同步AOF文件之后的所有写命令数据。

4. AOF文件的载入与数据还原

AOF文件包含了重建数据库状态所需的所有写命令,所以只需要读入并执行一遍命令,就可以还原之前的数据库状态。
读取AOF文件并还原数据库状态的步骤如下:

  1. 创建一个不带网络连接的伪客户端(fake client):因为redis的命令只能在客户端上下文中执行,而载入的命令来源于AOF文件不是网络连接。所以创建一个没有网络连接的伪客户端,这个执行命令的效果和带网络连接的客户端执行命令的效果完全一样。
  2. 从AOF文件中分析并读取出一条写命令。
  3. 使用伪客户端执行被读出的命令。
  4. 一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止。

5. AOF重写

背景是随着服务器运行,AOF文件的内容越来越多,文件体积过大。所以通过AOF文件重写,提供一个新的文件来代替,但体积小很多。

5.1 AOF文件重写的实现

该功能是通过读取服务器中的数据库状态来实现的,程序无须对现有aof文件进行任何读入,分析或者写入操作。使用aof_rewrite函数来实现。

同时,为了避免客户端输入缓冲区溢出,AOF重写的每一条命令的键不能超过64个(取REDIS_AOF_REWRITE_ITEMS_PER_CMD常量)。比如一个集合键元素过多,就会采用多条命令来记录。

5.2 AOF后台重写

上面aof_rewrite函数会进行大量写入,这个线程会被长时间阻塞。因为redis服务器使用单个线程来处理命令请求,所以在重写AOF文件期间,服务器无法处理客户端发来的命令请求。

所以,redis决定将AOF重写程序放入到子进程里执行,这样做可以达到两个目的:

  • 子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求;
  • 子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。

为了解决在重写过程中新产生的命令请求,redis新增了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当redis服务器执行完一个写命令之后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区,如下图所示。


aof重写缓冲区.png

也就是说在子进程执行AOF重写期间,服务器需要执行客户端发来的命令,将执行后的写命令追加到AOF缓冲区和AOF重写缓冲区。

当子进程完成AOF重写工作后,会向父进程发送一个信号,父进程接受到信号后,调用一个信号处理函数,执行以下工作:

  • 将AOF重写缓冲区中的所有内容写入到新的AOF文件中,这时新的AOF文件所保存的数据库状态将和服务器当前的数据库状态一致。
  • 对新的AOF文件进行改名,原子地覆盖现有AOF文件,完成新旧两个文件的替换。
    这个函数调用完成后,父进程就可以正常的接收命令请求了。在整个AOF后台重写过程中,只有信号处理函数会阻塞父进程,其他都不会,将AOF重写对服务器性能造成的影响降了最低。

6. 重点回顾

  • aof文件通过保存所有修改数据库的写命令请求来记录服务器的数据库状态。
  • aof文件中的所有命令都以redis命令请求协议的格式保存。
  • 命令请求会先保存到aof缓冲区里面,之后再定期写入并同步到aof文件。
  • appendfsync选项的不同值对aof持久化功能的安全性以及redis服务器的性能有很大的影响。
  • aof重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有aof文件进行任何读入,分析或者写入操作。
  • 在执行BGREWRITEAOF命令时,redis服务器会维护一个aof重写缓冲区,该缓冲区会在子进程创建新aof文件期间,记录服务器所执行的所有写命令。当子进程完成创建新的aof文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新aof文件的末尾,使得新旧两个aof文件所保存的数据库状态一致。最后,服务器用新的aof文件替换旧的aof文件,以此来完成aof文件重写操作。

7.触发AOF重写的条件(待补充)

  • AOF重写可以由用户通过调用BGREWRITEAOF手动触发。
  • 自动触发机制

redis如何保证持久化,突然断电情况下????

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

推荐阅读更多精彩内容

  • 从这篇文章开始,将依次介绍Redis高可用相关的知识——持久化、复制(及读写分离)、哨兵、以及集群。 本文将先说明...
    不变甄心阅读 692评论 0 4
  • 前言 在上一篇文章中,介绍了Redis内存模型,从这篇文章开始,将依次介绍Redis高可用相关的知识——持久化、复...
    Java架构阅读 2,306评论 3 21
  • 一、Redis高可用概述 在介绍Redis高可用之前,先说明一下在Redis的语境中高可用的含义。 我们知道,在w...
    空语阅读 1,596评论 0 2
  • 企业级redis集群架构的特点 海量数据 高并发 高可用 要达到高可用,持久化是不可减少的,持久化主要是做灾难恢复...
    lucode阅读 2,205评论 0 7
  • 如果人掉入黑洞,究竟是会被立即撕成碎片,还是会安然无恙?也许掉入黑洞并不一定意味着死亡,或许会变得比你想像中的更加...
    冬夏知多少阅读 160评论 0 0