Redis AOF 持久化 实现原理

1. AOF 概念

From now on, every time Redis receives a command that changes the dataset (e.g. SET)

it will append it to the AOF.

When you restart Redis it will re-play the AOF to rebuild the state.

写入的命令以追加到文件的形式保存到文件中 重启的时候对命令进行重现

AOF 是解决 RDB 会丢失一部分数据则出来的持久久,每写一条命令,就往文件中追加一次

但这样有可能会就会造成一个文件非常的大,并且在重启的时候,对命令重现的时候,非常的久

所以 AOF 也有类似的镜像模式

2. AOF 配置

appendonly no      #是否开启AOF,默认no

appendfilename "appendonly.aof"    #aof文件

appendfsync 

#always 每条命令都刷盘

#everysec 每秒刷盘

#no 内核决定什么时候刷

no-appendfsync-on-rewrite no  #是否开启rdb类似方式rewrite镜像写

auto-aof-rewrite-percentage 100  #AOF文件是上一次重写的时候的1倍大小

auto-aof-rewrite-min-size 64mb    #并于AOF文件要大于64M

aof-load-truncated yes  #当数据恢复的时候,假如遇到数据损坏,是继续还是直接退出

aof-rewrite-incremental-fsync yes #当 aof rewrite 的时候开启32MB数据,刷一次盘

3. AOF 在实现过程中重要成员

3.1 AOF 状态

/* AOF states */

#define REDIS_AOF_OFF 0            /* AOF 关闭 */

#define REDIS_AOF_ON 1              /* AOF 开启 */

#define REDIS_AOF_WAIT_REWRITE 2    /* 在线修改 为 appendonly yes 的时候 正在rewrite */

3.2 重要变量

# 要刷到AOF文件的数据,string类型

server.aof_buf     

# rewrite 期间,执行的命令缓存 ,是一个链表

server.aof_rewrite_buf_blocks

#距上次rewrite之后 AOF 刷了多少数据,每次rewrite之后清空

server.aof_current_size

#上一次rewrite的时候,AOF数据大小

server.aof_rewrite_base_size

4. AOF rewrite

假如 一直往一个文件中追加命令,那想想,那一个文件不是有可能无限大了?

再想想,假如一个key写一1000万次,那这key就得重现1000万次,那感觉是不是不爽?

所以作者也实现了 AOF rewrite,在一定情况下,将内存中的数据镜像的形式刷到文件,然后在那个时间点之后再进行 命令追加的形式保存

4.1 AOF rewrite 开启一个子进程

和 RDB 开启一个子进程一样,以 copy on write 的技术开启一个子进程

4.2 AOF rewrite 刷盘过程

4.2.1 创建一个 temp-rewriteaof-%d.aof 文件,并打开

4.2.2 将 aof rewrite 镜像数据以二进制形式追加到文件 temp-rewriteaof-%d.aof(这个过程是到内核)

4.2.3 每 32MB 数据将数据从内核fsync方式刷到磁盘

4.2.4 关闭句柄

4.2.5 将 temp-rewriteaof-%d.aof 改名为 temp-rewriteaof-bg-$pid.aof

4.3 AOF rewrite 子进程结束之后回调

在 aof rewrite 子进程结束之后,由主进程回调函数,根据 appendfsync 配置进行一次刷盘调用

1). 在rewrite期间新增的数据 追加到 temp-rewriteaof-bg-$pid.aof

2). 将AOF文件名修改为 appendonly.aof

3). aways 刷盘模式而主线程立马调aof_fsync 刷盘

4). everysec 刷盘模式而提交给另外一个线程异步刷盘

5). 提交给关闭句柄的线程,关闭 旧的AOF文件句柄

4.4 AOF rewrite 触发

4.4.1 定时触发

 /* Trigger an AOF rewrite if needed */

        if (server.rdb_child_pid == -1 &&

            server.aof_child_pid == -1 &&

            server.aof_rewrite_perc &&

            server.aof_current_size > server.aof_rewrite_min_size)

        {

            long long base = server.aof_rewrite_base_size ?

                            server.aof_rewrite_base_size : 1;

            long long growth = (server.aof_current_size*100/base) - 100;

            if (growth >= server.aof_rewrite_perc) {

                redisLog(REDIS_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);

                rewriteAppendOnlyFileBackground();

            }

        }

1). 默认100ms 检测一次

2). 没有rdb,aof rewrite 正在执行

3). 当前AOF数据大小 大于 设置的最小rewrite大小 ,默认64MB(auto-aof-rewrite-min-size参数)

4). (server.aof_current_size*100/base) - 100 >= server.aof_rewrite_perc 当前AOF数据大小 是否是上一次的多少倍,默认为 1 倍 (auto-aof-rewrite-percentage 参数)

4.4.2 手工触发 - BGREWRITEAOF 命令

void bgrewriteaofCommand(redisClient *c) {

    if (server.aof_child_pid != -1) {

        addReplyError(c,"Background append only file rewriting already in progress");

    } else if (server.rdb_child_pid != -1) {

        server.aof_rewrite_scheduled = 1;

        addReplyStatus(c,"Background append only file rewriting scheduled");

    } else if (rewriteAppendOnlyFileBackground() == REDIS_OK) {

        addReplyStatus(c,"Background append only file rewriting started");

    } else {

        addReply(c,shared.err);

    }

}

立马开启一个子进程进行rewrite

但命令结果返回给客户端之后不一定rewrite完了

4.4.3 手工触发 - CONFIG appendonly yes

 } else if (!strcasecmp(c->argv[2]->ptr,"appendonly")) {

        int enable = yesnotoi(o->ptr);

        if (enable == -1) goto badfmt;

        if (enable == 0 && server.aof_state != REDIS_AOF_OFF) {

            stopAppendOnly();

        } else if (enable && server.aof_state == REDIS_AOF_OFF) {

            if (startAppendOnly() == REDIS_ERR) {

                addReplyError(c,

                    "Unable to turn on AOF. Check server logs.");

                return;

            }

        }

    }

/* Called when the user switches from "appendonly no" to "appendonly yes"

* at runtime using the CONFIG command. */

int startAppendOnly(void) {

    server.aof_last_fsync = server.unixtime;

    server.aof_fd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);

    redisAssert(server.aof_state == REDIS_AOF_OFF);

    if (server.aof_fd == -1) {

        redisLog(REDIS_WARNING,"Redis needs to enable the AOF but can't open the append only file: %s",strerror(errno));

        return REDIS_ERR;

    }

    if (rewriteAppendOnlyFileBackground() == REDIS_ERR) {

        close(server.aof_fd);

        redisLog(REDIS_WARNING,"Redis needs to enable the AOF but can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.");

        return REDIS_ERR;

    }

    /* We correctly switched on AOF, now wait for the rerwite to be complete

    * in order to append data on disk. */

    server.aof_state = REDIS_AOF_WAIT_REWRITE;

    return REDIS_OK;

}

当原本是关闭的情况下

立马开一个子进程rewrite

假如rewrite失败了,每次定时任务都会尝试重试rewrite

5. Redis 写命令

每条写命令在更新了内存之后,都会有一个buf 来保存每命令的数据,不管 appendfsync 配置的是什么参数,这个buf 都是 Redis 自已的,并不是刷到内核里

1) . 只要开启了 AOF,就写到 server.aof_buf

2). 假如开启了rewrite进程 期间,将数据额外追加一份到 server.aof_rewrite_buf_blocks。这份数据是用于在 AOF rewrite 进程退出之后,立马将这份数据刷到 aof 文件末尾的

6. appendfsync everysec

 /* AOF postponed flush: Try at every cron cycle if the slow fsync

    * completed. */

    if (server.aof_flush_postponed_start) flushAppendOnlyFile(0);

    /* AOF write errors: in this case we have a buffer to flush as well and

    * clear the AOF error in case of success to make the DB writable again,

    * however to try every second is enough in case of 'hz' is set to

    * an higher frequency. */

    run_with_period(1000) {

        if (server.aof_last_write_status == REDIS_ERR)

            flushAppendOnlyFile(0);

    }

1). 每秒触发一次调用刷盘函数

2). 每次调用函数刷盘,会判断上一次刷盘是否完成,

如果没有完成,则100ms后的定时任务继续调用

3). 将数据从buf刷到内核

4). 提交给另外一个线程 异步调用aof_fsync 将数据从内核刷到磁盘

7. appendfsync aways

每条命令都刷盘一次

1). 每个事件处理之前 将server.aof_buf 里的数据刷到内核。记住并不是每条命令之后立马将数据落盘,而是等待下一个事件执行之前落盘

2). 主线程调用 aof_fsync 从内核刷到磁盘。这里是由主线程执行调用 fsync 刷到磁盘

8. appendfsync no

1). 每个事件处理之前 将server.aof_buf 里的数据刷到内核

2). 将 buf 刷到内核之后,将由内核决定什么时候刷到磁盘,redis 进程不再接管flush这个事情

9. 小结

1). AOF 也会有rewrite刷盘,也是开子进程,cony on write

2). AOF 默认情况下是 当前AOF数据大小是上一次rewrite的倍的时候触发

3). AOF everysec 刷盘,是每秒从buf刷到内核后fsync刷到磁盘, 并不是每条命令就往内核里刷一次数据

4). AOF aways 刷盘,是写命令后的下一个事件执行之前由主线程执行刷盘的,并非写命令之后立马刷盘

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

推荐阅读更多精彩内容

  • 一、Redis高可用概述 在介绍Redis高可用之前,先说明一下在Redis的语境中高可用的含义。 我们知道,在w...
    空语阅读 1,593评论 0 2
  • AOF简介 Redis拥有者两种的持久化存储的方式一中是rdb序列化存储db的信息另外一种就是aof.Append...
    烨哥阅读 4,173评论 0 3
  • 简介 AOF 持久化和 RDB 持久化的最主要区别在于,前者记录了数据的变更,而后者是保存了数据本身。本篇主要讲的...
    翼徳阅读 605评论 0 8
  • 说明 当redis存储非临时数据时,为了降低redis故障而引起的数据丢失,redis提供了AOF(Append ...
    不拘绳墨阅读 299评论 1 0
  • 企业级redis集群架构的特点 海量数据 高并发 高可用 要达到高可用,持久化是不可减少的,持久化主要是做灾难恢复...
    lucode阅读 2,191评论 0 7