ホームページ >データベース >Redis >Redis の深層学習の永続化原理の詳細な説明

Redis の深層学習の永続化原理の詳細な説明

青灯夜游
青灯夜游転載
2022-03-10 10:54:512228ブラウズ

この記事では、Redis について詳しく学び、永続性を詳細に分析し、その動作原理や永続化プロセスなどを紹介します。お役に立てば幸いです。

この記事では、次の側面から Redis の永続化メカニズムを紹介します。

## 前に書いてあります

この記事は全体的に詳しく説明されていますRedis の 2 つの永続化メソッドを、動作原理、永続化プロセス、実践的な戦略、およびその背後にある理論的知識を含めて紹介します。前回の記事ではRDBの永続化についてのみ紹介しましたが、Redisの永続化は全体であり個別に紹介できないため再整理しました。 [関連する推奨事項: Redis ビデオ チュートリアル ]

Redis はインメモリ データベースです。すべてのデータはメモリに保存されます。これは、MySQL、Oracle、 Redis はハードディスクにデータを保存する場合と比べて、読み書き効率が非常に高いです。ただし、メモリへの保存には大きな欠点があり、電源が切れたり、コンピュータがダウンしたりすると、メモリ データベースの内容はすべて失われます。この欠点を補うために、Redis では、メモリ データをハードディスク ファイルに永続化し、バックアップ ファイルを通じてデータを復元する機能 (Redis 永続化メカニズム) を提供します。

Redis は、RDB スナップショットと AOF という 2 つの永続化方法をサポートしています。

RDB 永続性

RDB スナップショット 正式な言葉: RDB 永続性ソリューションは、指定された時間間隔でデータ セットに対して生成されるポイントツータイム スナップショットです。特定の時点での Redis データベース内のすべてのデータ オブジェクトのメモリ スナップショットを圧縮バイナリ ファイルに保存します。これは、Redis データのバックアップ、転送、リカバリに使用できます。これまでのところ、これはまだ公式のデフォルトのサポート ソリューションです。

RDB の動作原理

RDB は Redis 内のデータセットの特定時点のスナップショットであるため、まず Redis 内のデータ オブジェクトがどのようにメモリ内に保存され編成されるかを簡単に理解しましょう。

デフォルトでは、Redis には 16 のデータベースがあり、0 ~ 15 の番号が付けられています。各 Redis データベースは redisDb オブジェクトによって表され、redisDb はハッシュテーブル ストレージ K-V オブジェクトを使用します。理解を容易にするために、データベースの 1 つを例として、Redis の内部データのストレージ構造の概略図を描きました。

Redis の深層学習の永続化原理の詳細な説明
ポイントインタイム スナップショットは、特定の瞬間における Redis の各 DB 内の各データ オブジェクトのステータスです。データ オブジェクトは変更されなくなりました 。これらのデータ オブジェクトを 1 つずつ読み取り、上図のデータ構造関係に従ってファイルに書き込むことで、Redis の永続性を実現できます。その後、Redis が再起動すると、このファイルの内容がルールに従って読み取られ、Redis メモリに書き込まれて永続的な状態に復元されます。

もちろん、上記の仮定が正しい場合、この前提は当てはまります。そうでない場合、常に変化するデータセットに直面して開始する方法はありません。 Redis でのクライアント コマンド処理はシングルスレッド モデルであることがわかっていますが、永続化がコマンドとして処理される場合、データ セットは間違いなく静的な状態になります。さらに、オペレーティング システムが提供する fork() 関数によって作成された子プロセスは、親プロセスと同じメモリ データを取得できます。これはメモリ データのコピーを取得することと同じであり、フォークが完了した後、親プロセスが行うと、状態を永続化する作業が子プロセスに渡されるので、それを子プロセスに渡すだけです。

明らかに、最初の状況はお勧めできません。永続的なバックアップにより、Redis サービスが短期間利用できなくなりますが、これは高 HA システムでは耐えられません。したがって、2 番目の方法が RDB 永続化の主な実用的な方法になります。子プロセスをフォークした後も親プロセスのデータは変化し続け、子プロセスは親プロセスと同期していないため、RDB 永続化はリアルタイム性を保証できず、RDB 永続化完了後に停電やダウンタイムが発生すると、部分的なデータ損失、バックアップ 頻度によって失われたデータの量が決まります。バックアップ頻度を増やすと、フォーク プロセスがより多くの CPU リソースを消費し、ディスク I/O も大きくなります。

永続化プロセス

Redis で RDB 永続化を完了するには、rdbSave と rdbSaveBackground (ソース コード ファイル rdb.c 内) の 2 つの方法があります。この 2 つの違いについて簡単に説明します。

  • rdbSave: 同期的に実行され、メソッドが呼び出された直後に永続化プロセスが開始されます。 Redis はシングルスレッド モデルであるため、永続化プロセス中にブロックされ、Redis は外部サービスを提供できません;
  • rdbSaveBackground: バックグラウンド (非同期) で実行されます。このメソッドは子をフォークアウトします。実際の永続化プロセスは、子プロセス (rdbSave の呼び出し) で実行されると、メイン プロセスがサービスを提供し続けます;

RDB 永続化のトリガーは、上記の 2 つのメソッドから分離できない必要があります。トリガー方法は手動と自動に分けられます。手動トリガーは理解しやすいです。これは、Redis クライアントを通じて Redis サーバーへの永続化バックアップ命令を手動で開始し、Redis サーバーが永続化プロセスの実行を開始することを意味します。ここでの命令には、save と bgsave が含まれます。自動トリガーは、独自の動作要件に基づいて事前に設定された条件が満たされたときに Redis が自動的にトリガーする永続化プロセスです。自動トリガーされるシナリオは次のとおりです (この記事から抜粋):

  • serverCron 中 save m n構成ルールは自動的にトリガーされます;
  • スレーブ ノードが完全にコピーされると、マスター ノードは rdb ファイルをスレーブ ノードに送信してコピー操作を完了し、マスター ノードは bgsave を開始します。 ;
  • 実行debug reloadコマンドが redis を再ロードするとき;
  • デフォルト (AOF が有効になっていない) では、shutdown コマンドが実行されると、bgsave が自動的に実行されます。

ソース コードと参考記事を組み合わせて、全体的な理解を助けるために RDB 永続化プロセスを整理し、その後、いくつか詳細に説明します。
Redis の深層学習の永続化原理の詳細な説明
上の図からわかるように:

  • 自動的にトリガーされる RDB 永続化は、rdbSaveBackground を通じてサブプロセスで実行される永続化戦略です。 ;
  • 手動トリガーは、save コマンドや bgsave コマンドなどのクライアント コマンドによってトリガーされます。保存コマンドは、Redis コマンド処理スレッドの rdbSave メソッドをブロック的に呼び出すことで完了します。

自動トリガー プロセスは、rdbSaveBackground、rdbSave などをカバーする完全なリンクです。次に、プロセス全体を分析するための例として、serverCron を使用します。

保存ルールとチェック

serverCron は Redis の定期関数であり、100 ミリ秒ごとに実行されます。そのジョブの 1 つは、構成ファイル内の保存ルールに基づいて現在の状況を判断することです。自動永続化プロセスが必要であり、条件が満たされると永続化の開始が試行されます。この部分の実装について学習します。

redisServer には RDB の永続化に関連するフィールドがいくつかあります。コードからそれらを抽出し、中国語と英語で調べました:

struct redisServer {
    /* 省略其他字段 */ 
    /* RDB persistence */
    long long dirty;                /* Changes to DB from the last save
                                     * 上次持久化后修改key的次数 */
    struct saveparam *saveparams;   /* Save points array for RDB,
                                     * 对应配置文件多个save参数 */
    int saveparamslen;              /* Number of saving points,
                                     * save参数的数量 */
    time_t lastsave;                /* Unix time of last successful save 
                                     * 上次持久化时间*/
    /* 省略其他字段 */
}

/* 对应redis.conf中的save参数 */
struct saveparam {
    time_t seconds;                    /* 统计时间范围 */   
    int changes;                    /* 数据修改次数 */
};

saveparams は、redis.conf の保存ルールに対応します。保存パラメータは、Redis が自動バックアップをトリガーするためのトリガー戦略です。は統計時間 (単位: 秒)、changes は、統計時間内に発生した書き込みの数です。 save m n は、m 秒以内に n 回の書き込みがある場合、スナップショット、つまりバックアップがトリガーされることを意味します。保存パラメータの複数のグループを構成して、さまざまな条件下でのバックアップ要件を満たすことができます。 RDB の自動バックアップ ポリシーをオフにする必要がある場合は、save "" を使用できます。以下に、いくつかの構成について説明します。

# 表示900秒(15分钟)内至少有1个key的值发生变化,则执行
save 900 1
# 表示300秒(5分钟)内至少有1个key的值发生变化,则执行
save 300 10
# 表示60秒(1分钟)内至少有10000个key的值发生变化,则执行
save 60 10000
# 该配置将会关闭RDB方式的持久化
save ""

serverCronRDB 保存ルールの検出コードは次のとおりです。

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    /* 省略其他逻辑 */
    
    /* 如果用户请求进行AOF文件重写时,Redis正在执行RDB持久化,Redis会安排在RDB持久化完成后执行AOF文件重写,
     * 如果aof_rewrite_scheduled为true,说明需要执行用户的请求 */
    /* Check if a background saving or AOF rewrite in progress terminated. */
    if (hasActiveChildProcess() || ldbPendingChildren())
    {
        run_with_period(1000) receiveChildInfo();
        checkChildrenDone();
    } else {
        /* 后台无 saving/rewrite 子进程才会进行,逐个检查每个save规则*/
        for (j = 0; j = sp->changes 
                && server.unixtime-server.lastsave > sp->seconds 
                &&(server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY || server.lastbgsave_status == C_OK))
            {
                serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...", sp->changes, (int)sp->seconds);
                rdbSaveInfo rsi, *rsiptr;
                rsiptr = rdbPopulateSaveInfo(&rsi);
                /* 执行bgsave过程 */
                rdbSaveBackground(server.rdb_filename,rsiptr);
                break;
            }
        }

        /* 省略:Trigger an AOF rewrite if needed. */
    }
    /* 省略其他逻辑 */
}
バックグラウンドでの RDB 永続性または AOF がない場合replication プロセスを作成する際、serverCron は上記の設定とステータスに基づいて永続化操作を実行する必要があるかどうかを判断します。判断基準は、lastsave と Dirty が saveparams 配列の条件のいずれかを満たしているかどうかです。条件が一致すると、rdbSaveBackground メソッドが呼び出され、非同期永続化処理が実行されます。

rdbSaveBackground

rdbSaveBackground は RDB 永続化のための補助メソッドです。その主な仕事は子プロセスをフォークすることであり、呼び出し元 (親プロセスまたは子プロセス) に応じて 2 つの異なる実行ロジックがあります。 )。

    呼び出し元が親プロセスの場合は、子プロセスをフォークアウトし、子プロセスの情報を保存して、直接戻ります。
  • 呼び出し元が子プロセスの場合は、rdbSave を呼び出して RDB 永続化ロジックを実行し、永続化が完了したら子プロセスを終了します。
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
    pid_t childpid;

    if (hasActiveChildProcess()) return C_ERR;

    server.dirty_before_bgsave = server.dirty;
    server.lastbgsave_try = time(NULL);

    // fork子进程
    if ((childpid = redisFork(CHILD_TYPE_RDB)) == 0) {
        int retval;

        /* Child 子进程:修改进程标题 */
        redisSetProcTitle("redis-rdb-bgsave");
        redisSetCpuAffinity(server.bgsave_cpulist);
        // 执行rdb持久化
        retval = rdbSave(filename,rsi);
        if (retval == C_OK) {
            sendChildCOWInfo(CHILD_TYPE_RDB, 1, "RDB");
        }
        // 持久化完成后,退出子进程
        exitFromChild((retval == C_OK) ? 0 : 1);
    } else {
        /* Parent 父进程:记录fork子进程的时间等信息*/
        if (childpid == -1) {
            server.lastbgsave_status = C_ERR;
            serverLog(LL_WARNING,"Can't save in background: fork: %s",
                strerror(errno));
            return C_ERR;
        }
        serverLog(LL_NOTICE,"Background saving started by pid %ld",(long) childpid);
        // 记录子进程开始的时间、类型等。
        server.rdb_save_time_start = time(NULL);
        server.rdb_child_type = RDB_CHILD_TYPE_DISK;
        return C_OK;
    }
    return C_OK; /* unreached */
}
rdbSave は、真に永続化を実行するメソッドです。実行中に大量の I/O および計算操作が含まれるため、時間がかかり、多くの CPU を消費します。永続化されます。 Redis のシングルスレッド モデルでは、プロセスがスレッド リソースを占有し続けるため、Redis は他のサービスを提供できなくなります。この問題を解決するために、Redis は rdbSaveBackground で子プロセスをフォークアウトし、子プロセスが永続化作業を完了して、親プロセスのリソースを過剰に占有することを回避します。

親プロセスのメモリ使用量が大きすぎると、フォーク処理に時間がかかることに注意してください。この処理中、親プロセスは外部サービスを提供できません。また、コンピュータは子プロセスをフォークする前にメモリ使用量を総合的に考慮する必要があり、メモリ リソースが 2 倍占有されるため、十分なメモリを確保する必要があります。 info stats コマンドでlatest_fork_usec オプションを確認し、最新のフォーク操作にかかった時間を取得します。

rdbSave

Redis的rdbSave函数是真正进行RDB持久化的函数,流程、细节贼多,整体流程可以总结为:创建并打开临时文件、Redis内存数据写入临时文件、临时文件写入磁盘、临时文件重命名为正式RDB文件、更新持久化状态信息(dirty、lastsave)。其中“Redis内存数据写入临时文件”最为核心和复杂,写入过程直接体现了RDB文件的文件格式,本着一图胜千言的理念,我按照源码流程绘制了下图。
Redis の深層学習の永続化原理の詳細な説明
补充说明一下,上图右下角“遍历当前数据库的键值对并写入”这个环节会根据不同类型的Redis数据类型及底层数据结构采用不同的格式写入到RDB文件中,不再展开了。我觉得大家对整个过程有个直观的理解就好,这对于我们理解Redis内部的运作机制大有裨益。

AOF持久化

上一节我们知道RDB是一种时间点(point-to-time)快照,适合数据备份及灾难恢复,由于工作原理的“先天性缺陷”无法保证实时性持久化,这对于缓存丢失零容忍的系统来说是个硬伤,于是就有了AOF。

AOF工作原理

AOF是Append Only File的缩写,它是Redis的完全持久化策略,从1.1版本开始支持;这里的file存储的是引起Redis数据修改的命令集合(比如:set/hset/del等),这些集合按照Redis Server的处理顺序追加到文件中。当重启Redis时,Redis就可以从头读取AOF中的指令并重放,进而恢复关闭前的数据状态。

AOF持久化默认是关闭的,修改redis.conf以下信息并重启,即可开启AOF持久化功能。

# no-关闭,yes-开启,默认no
appendonly yes
appendfilename appendonly.aof

AOF本质是为了持久化,持久化对象是Redis内每一个key的状态,持久化的目的是为了在Reids发生故障重启后能够恢复至重启前或故障前的状态。相比于RDB,AOF采取的策略是按照执行顺序持久化每一条能够引起Redis中对象状态变更的命令,命令是有序的、有选择的。把aof文件转移至任何一台Redis Server,从头到尾按序重放这些命令即可恢复如初。举个例子:

首先执行指令set number 0,然后随机调用incr numberget number 各5次,最后再执行一次get number ,我们得到的结果肯定是5。

因为在这个过程中,能够引起number状态变更的只有set/incr类型的指令,并且它们执行的先后顺序是已知的,无论执行多少次get都不会影响number的状态。所以,保留所有set/incr命令并持久化至aof文件即可。按照aof的设计原理,aof文件中的内容应该是这样的(这里是假设,实际为RESP协议):

set number 0
incr number
incr number
incr number
incr number
incr number

最本质的原理用“命令重放”四个字就可以概括。但是,考虑实际生产环境的复杂性及操作系统等方面的限制,Redis所要考虑的工作要比这个例子复杂的多:

  • Redis Server启动后,aof文件一直在追加命令,文件会越来越大。文件越大,Redis重启后恢复耗时越久;文件太大,转移工作就越难;不加管理,可能撑爆硬盘。很显然,需要在合适的时机对文件进行精简。例子中的5条incr指令很明显的可以替换为为一条set命令,存在很大的压缩空间。
  • 众所周知,文件I/O是操作系统性能的短板,为了提高效率,文件系统设计了一套复杂的缓存机制,Redis操作命令的追加操作只是把数据写入了缓冲区(aof_buf),从缓冲区到写入物理文件在性能与安全之间权衡会有不同的选择。
  • 文件压缩即意味着重写,重写时即可依据已有的aof文件做命令整合,也可以先根据当前Redis内数据的状态做快照,再把存储快照过程中的新增的命令做追加。
  • aof备份后的文件是为了恢复数据,结合aof文件的格式、完整性等因素,Redis也要设计一套完整的方案做支持。

持久化流程

从流程上来看,AOF的工作原理可以概括为几个步骤:命令追加(append)、文件写入与同步(fsync)、文件重写(rewrite)、重启加载(load),接下来依次了解每个步骤的细节及背后的设计哲学。
Redis の深層学習の永続化原理の詳細な説明

Command Append

AOF 永続化機能がオンになっている場合、Redis は書き込みコマンドを実行した後、プロトコル形式 (つまり、Redis クライアントと Redis クライアントとの間の対話用の通信プロトコル RESP) を使用します。サーバー) 実行された書き込みコマンドを、Redis サーバーによって維持される AOF バッファーの末尾に追加します。 AOF ファイルの追加操作はシングルスレッドのみであり、シークなどの複雑な操作はなく、停電やダウンタイムが発生してもファイルが破損する心配はありません。さらに、テキスト プロトコルを使用することには多くの利点があります:

  • テキスト プロトコルには優れた互換性があります;
  • テキスト プロトコルはクライアントの要求コマンドであり、二次処理を必要としません。ストレージと読み込み時の処理オーバーヘッドを節約します;
  • テキスト プロトコルは読みやすく、表示や変更などに便利です。

AOF バッファ タイプは、Redis sds によって独自に設計されたデータ構造です。Redis はコマンドの種類 (catAppendOnlyGenericCommand、#) に応じて異なるメソッドを使用します。 ## catAppendOnlyExpireAtCommand など) はコマンドの内容を処理し、最終的にバッファーに書き込みます。

コマンドを追加するときに AOF 書き換えが進行中の場合、これらのコマンドは書き換えバッファ (

aof_rewrite_buffer) にも追加されることに注意してください。

ファイルの書き込みと同期

AOF ファイルの書き込みと同期は、オペレーティング システムのサポートから切り離すことはできません。導入を開始する前に、Linux I/O に関する知識を追加する必要があります。バッファー。ハードディスクのI/O性能が悪く、ファイルの読み書き速度がCPUの処理速度に比べてはるかに劣るため、ファイルを書き込むたびにハードディスクへのデータの書き込みを待っていると、オペレーティング システムの全体的なパフォーマンスが低下します。この問題を解決するために、オペレーティング システムは、ハード ディスクの I/O パフォーマンスを向上させる遅延書き込みメカニズムを提供します。


Redis の深層学習の永続化原理の詳細な説明

従来の UNIX 実装では、カーネル内にバッファ キャッシュまたはページ キャッシュがあり、ほとんどのディスク I/O はバッファリングを通じて発生します。ファイルにデータを書き込むとき、カーネルは通常、最初にデータをバッファの 1 つにコピーします。バッファがまだいっぱいでない場合は、出力キューにキューに入れず、バッファがいっぱいになるまで、またはカーネルが必要とするときに待ちます。バッファが他のディスク ブロック データを格納するために再利用される場合、バッファは出力キューに入れられ、実際の I/O 操作はキューの先頭に達したときにのみ実行されます。この出力方法を遅延書き込みと呼びます。
書き込みを遅延すると、ディスクの読み取りおよび書き込みの回数が減りますが、ファイルの内容の更新速度も低下するため、ファイルに書き込まれるデータが一定期間ディスクに書き込まれなくなります。システム障害が発生すると、この遅延によりファイルの更新が失われる可能性があります。ディスク上の実際のファイル システムとバッファ キャッシュ内のコンテンツの一貫性を確保するために、UNIX システムは 3 つの関数 (sync、fsync、および fdatasync) を提供し、ハード ディスクへの強制書き込みをサポートします。

Redis は、各イベントのローテーションが終了する前 (

beforeSleep) に関数 flushAppendOnlyFile を呼び出し、flushAppendOnlyFile が AOF バッファー (aof_buf) をフラッシュします。 ) はカーネル バッファに書き込まれ、カーネル バッファ内のデータをディスクに書き込むために使用される戦略は、appendfsync 構成に基づいて決定されます。つまり、fsync()# を呼び出します。 ##。この構成には 3 つのオプション オプション alwaysnoeverysec があり、詳細は次のとおりです。

always: 毎回呼び出されます。
    fsync()
  • は、セキュリティが最も高く、パフォーマンスが最悪の戦略です。 いいえ:
  • fsync()
  • は呼び出されません。最高のパフォーマンス、最悪のセキュリティ。 everysec: 同期条件が満たされた場合にのみ
  • fsync()
  • を呼び出します。これは公式に推奨される同期戦略であり、デフォルトの構成でもあります。パフォーマンスとデータ セキュリティの両方が考慮されています。理論的には、突然のシステム シャットダウンが発生した場合でも、データが失われるのは 1 秒だけです。
注: 上記で紹介した戦略は、設定項目

no-appendfsync-on-rewrite の影響を受けます。その機能は、AOF ファイルの書き換え中かどうかを Redis に通知することです。 fsync() の呼び出しを無効にします。デフォルトは no です。

appendfsync

always または everysec に設定されている場合、BGSAVE または は進行中です。バックグラウンドの BGREWRITEAOF はディスク I/O を大量に消費します。特定の Linux システム構成では、Redis による fsync() の呼び出しが長時間ブロックされる可能性があります。ただし、fsync() が別のスレッドで実行された場合でも同期書き込み操作はブロックされるため、この問題はまだ修正されていません。 この問題を軽減するには、このオプションを使用して、

BGSAVE

または BGREWRITEAOF の実行中にメイン プロセスで fsync() が呼び出されないようにすることができます。 <ul> <li>设置为<code>yes意味着,如果子进程正在进行BGSAVEBGREWRITEAOF,AOF的持久化能力就与appendfsync设置为no有着相同的效果。最糟糕的情况下,这可能会导致30秒的缓存数据丢失。

  • 如果你的系统有上面描述的延迟问题,就把这个选项设置为yes,否则保持为no
  • 文件重写

    如前面提到的,Redis长时间运行,命令不断写入AOF,文件会越来越大,不加控制可能影响宿主机的安全。

    为了解决AOF文件体积问题,Redis引入了AOF文件重写功能,它会根据Redis内数据对象的最新状态生成新的AOF文件,新旧文件对应的数据状态一致,但是新文件会具有较小的体积。重写既减少了AOF文件对磁盘空间的占用,又可以提高Redis重启时数据恢复的速度。还是下面这个例子,旧文件中的6条命令等同于新文件中的1条命令,压缩效果显而易见。
    Redis の深層学習の永続化原理の詳細な説明
    我们说,AOF文件太大时会触发AOF文件重写,那到底是多大呢?有哪些情况会触发重写操作呢?
    **
    与RDB方式一样,AOF文件重写既可以手动触发,也会自动触发。手动触发直接调用bgrewriteaof命令,如果当时无子进程执行会立刻执行,否则安排在子进程结束后执行。自动触发由Redis的周期性方法serverCron检查在满足一定条件时触发。先了解两个配置项:

    • auto-aof-rewrite-percentage:代表当前AOF文件大小(aof_current_size)和上一次重写后AOF文件大小(aof_base_size)相比,增长的比例。
    • auto-aof-rewrite-min-size:表示运行BGREWRITEAOF时AOF文件占用空间最小值,默认为64MB;

    Redis启动时把aof_base_size初始化为当时aof文件的大小,Redis运行过程中,当AOF文件重写操作完成时,会对其进行更新;aof_current_sizeserverCron执行时AOF文件的实时大小。当满足以下两个条件时,AOF文件重写就会触发:

    增长比例:(aof_current_size - aof_base_size) / aof_base_size > auto-aof-rewrite-percentage
    文件大小:aof_current_size > auto-aof-rewrite-min-size

    手动触发与自动触发的代码如下,同样在周期性方法serverCron中:

    int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
        /* 省略其他逻辑 */
        
        /* 如果用户请求进行AOF文件重写时,Redis正在执行RDB持久化,Redis会安排在RDB持久化完成后执行AOF文件重写,
         * 如果aof_rewrite_scheduled为true,说明需要执行用户的请求 */
        if (!hasActiveChildProcess() &&
            server.aof_rewrite_scheduled)
        {
            rewriteAppendOnlyFileBackground();
        }
    
        /* Check if a background saving or AOF rewrite in progress terminated. */
        if (hasActiveChildProcess() || ldbPendingChildren())
        {
            run_with_period(1000) receiveChildInfo();
            checkChildrenDone();
        } else {
            /* 省略rdb持久化条件检查 */
    
            /* AOF重写条件检查:aof开启、无子进程运行、增长百分比已设置、当前文件大小超过阈值 */
            if (server.aof_state == AOF_ON &&
                !hasActiveChildProcess() &&
                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) {
                    serverLog(LL_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);
                    rewriteAppendOnlyFileBackground();
                }
            }
        }
        /**/
    }

    AOF文件重写的流程是什么?听说Redis支持混合持久化,对AOF文件重写有什么影响?

    从4.0版本开始,Redis在AOF模式中引入了混合持久化方案,即:纯AOF方式、RDB+AOF方式,这一策略由配置参数aof-use-rdb-preamble(使用RDB作为AOF文件的前半段)控制,默认关闭(no),设置为yes可开启。所以,在AOF重写过程中文件的写入会有两种不同的方式。当aof-use-rdb-preamble的值是:

    • no:按照AOF格式写入命令,与4.0前版本无差别;
    • yes:先按照RDB格式写入数据状态,然后把重写期间AOF缓冲区的内容以AOF格式写入,文件前半部分为RDB格式,后半部分为AOF格式。

    结合源码(6.0版本,源码太多这里不贴出,可参考aof.c)及参考资料,绘制AOF重写(BGREWRITEAOF)流程图:
    Redis の深層学習の永続化原理の詳細な説明
    结合上图,总结一下AOF文件重写的流程:

    • rewriteAppendOnlyFileBackground开始执行,检查是否有正在进行的AOF重写或RDB持久化子进程:如果有,则退出该流程;如果没有,则继续创建接下来父子进程间数据传输的通信管道。执行fork()操作,成功后父子进程分别执行不同的流程。
    • 父进程:

      • 记录子进程信息(pid)、时间戳等;
      • 继续响应其他客户端请求;
      • 收集AOF重写期间的命令,追加至aof_rewrite_buffer;
      • 等待并向子进程同步aof_rewrite_buffer的内容;
    • 子进程:

      • 現在のプロセス名を変更し、書き換えに必要な一時ファイルを作成し、rewriteAppendOnlyFile 関数を呼び出します;
      • aof-use-rdb-preamble 構成に従って、 RDB または AOF を使用する 前半を AOF モードで書き込み、ハードディスクに同期する;
      • 親プロセスからインクリメンタル AOF コマンドを受け取り、後半を AOF モードで書き込み、ハードディスクに同期する;
      • AOF ファイルの名前を変更すると、子プロセスが終了します。

    データの読み込み

    Redis の起動後、loadDataFromDisk 関数を通じてデータの読み込み作業が実行されます。ここで注意すべき点は、永続化メソッドは AOF、RDB、またはその両方を使用できますが、データをロードするときに選択する必要があり、2 つのメソッドを別々にロードすると混乱が生じることになります。

    理論的には、AOF 永続性は RDB よりも優れたリアルタイム パフォーマンスを備えています。AOF 永続性が有効になっている場合、Redis はデータのロード時に AOF を優先します。さらに、Redis 4.0 バージョン以降、AOF はハイブリッド永続性をサポートするため、AOF ファイルをロードするときにバージョンの互換性を考慮する必要があります。 Redis データの読み込みプロセスを次の図に示します。
    Redis の深層学習の永続化原理の詳細な説明
    AOF モードでは、ハイブリッド永続化メカニズムをオンにすることによって生成されるファイルは「RDB head AOF tail」です。電源が入っていない場合は「」が発生し、ファイルはすべてAOF形式です。 2 つのファイル形式の互換性を考慮して、AOF ファイルが RDB ヘッダーであると Redis が検出した場合、RDB データ ロード メソッドを使用して前半を読み取り、復元し、次に AOF メソッドを使用して後半を読み取り、復元します。 。 AOF 形式で保存されたデータは RESP プロトコル コマンドであるため、Redis は擬似クライアントを使用してコマンドを実行し、データを回復します。

    AOF コマンドの追加プロセス中にダウンタイムが発生した場合、遅延書き込みの技術的特性により、AOF の RESP コマンドが不完全 (切り捨て) になる可能性があります。この状況が発生した場合、Redis は構成項目 aof-load-truncated に従ってさまざまな処理戦略を実行します。この構成は、Redis に起動時に aof ファイルを読み取るように指示し、ファイルが切り詰められている (不完全である) ことが判明した場合にどうするかを指示します:

    • #yes: できるだけ多くのデータをロードし、ログに記録します。ユーザー;
    • no: システム エラーでクラッシュし、起動が禁止されます。ユーザーはファイルを修復してから再起動する必要があります。

    概要

    Redis は 2 つの永続化オプションを提供します: RDB は、特定の実質的な間隔でデータ セットのポイントインタイム スナップショットの生成をサポートします; AOF は、Redis サーバーが受信した各データを保存します書き込みコマンドはログに保存され、Redis の再起動時にコマンドを再実行することでデータを復元できます。ログ形式は RESP プロトコルであり、ログ ファイルに対して追加操作のみが実行されるため、破損の危険はありません。また、AOF ファイルが大きすぎる場合、圧縮ファイルを自動的に書き換えることができます。

    もちろん、データを永続化する必要がない場合は、Redis の永続化機能を無効にすることもできますが、これはほとんどの場合には当てはまりません。実際、RDB と AOF を同時に使用することがありますが、最も重要なことは、RDB と AOF を合理的に使用するために 2 つの違いを理解することです。

    RDB と AOF

    RDB の利点

    • RDB は、特定の時点での Redis データのスナップショットを表すコンパクトな圧縮バイナリ ファイルです。バックアップ、フルコピー、その他のシナリオ。
    • RDB は災害復旧やデータ移行に非常に適しており、RDB ファイルを必要な場所に移動して再ロードできます。
    • RDB は Redis データのメモリ スナップショットであり、AOF コマンドの再生よりもデータの回復速度が速く、パフォーマンスが高くなります。

    RDB の欠点

    • RDB メソッドは、リアルタイムまたは第 2 レベルの永続性を実現できません。永続化プロセスは子プロセスをフォークした後に子プロセスによって完了するため、子プロセスのメモリはフォーク操作の時点での親プロセスのデータのスナップショットにすぎず、フォーク操作後も親プロセスは続行されます。内部データは常に変化しており、データは更新されず、両者の間には常に差異が存在するため、リアルタイムのパフォーマンスを実現できません。
    • RDB 永続化プロセス中のフォーク操作ではメモリ使用量が 2 倍になり、親プロセスのデータが増えるほどフォーク プロセスに時間がかかります。
    • Redis リクエストの同時実行性が高いと、保存ルールに頻繁にヒットし、フォーク操作や永続的なバックアップの頻度が制御不能になる可能性があります。
    • RDB ファイルにはファイル形式要件があり、Redis のバージョンが異なると、ファイル形式を変更するため調整が行われ、旧バージョンと新バージョンの互換性がなくなる問題が発生します。

    AOF の利点

    • AOF 永続化によりリアルタイム パフォーマンスが向上します。3 つの異なる方法 (appendfsync) を選択できます: no、毎秒、常に、毎 デフォルトとして極端な場合には、1 秒間のデータが失われる可能性があります。
    • AOF ファイルには追加操作のみがあり、複雑なシークやその他のファイル操作はなく、損傷の危険はありません。最後に書き込まれたデータが切り詰められている場合でも、redis-check-aof ツールを使用して簡単に修復できます。
    • AOF ファイルが大きくなると、Redis がバックグラウンドで自動的に書き換えることができます。 。書き換え処理中は古いファイルの書き込みが継続されますが、書き換えが完了すると新しいファイルは小さくなり、書き換え処理中の増分コマンドも新しいファイルに追加されます。
    • AOF ファイルには、Redis 内のデータに対するすべての操作コマンドが、理解しやすく解析しやすい形で含まれています。たとえ誤って全データを消去してしまったとしても、AOFファイルを書き換えない限り、最後のコマンドを削除することで全データを取り戻すことができます。
    • AOF はすでにハイブリッド永続性をサポートしており、ファイル サイズを効果的に制御でき、データ読み込みの効率が向上しています。

    AOF の欠点

    • 同じデータ収集の場合、AOF ファイルは通常、RDB ファイルより大きくなります;
    • 特定の fsync ポリシーでは、AOF はわずかに大きくなります。 RDBよりも遅いです。一般的に言えば、fsync_every_sec のパフォーマンスは依然として非常に高く、fsync_no のパフォーマンスは RDB に匹敵します。ただし、書き込みプレッシャーが非常に大きい場合、RDB は最大の低レイテンシ保証を提供できます。
    • AOF で、Redis はかつて、RDB ではほとんど遭遇することが不可能ないくつかのまれなバグに遭遇しました。一部の特別な命令 (BRPOPLPUSH など) により、リロードされたデータが永続化前のデータと不一致になることがあり、Redis 担当者はかつて同じ条件でテストしましたが、問題は再現できませんでした。

    使用上の提案

    RDB と AOF の 2 つの永続化方式の動作原理、実行プロセス、長所と短所を理解した後、長所と短所を比較検討する方法を考えてみましょう。実際のシナリオでは、2 つの永続化メソッドを合理的に使用します。 Redis をキャッシュツールとしてのみ使用する場合は、永続化データベースに基づいてすべてのデータを再構築することができ、永続化機能をオフにして、予熱、キャッシュの侵入、ブレークダウン、アバランチなどの保護作業を行うことができます。

    通常の状況では、Redis は分散ロック、ランキング、登録センターなど、より多くの作業を引き受けます。永続化機能は、災害復旧やデータ移行においてより大きな役割を果たします。

    • Redis をデータベースとして使用しないでください。すべてのデータは、可能な限りアプリケーション サービスによって自動的に再構築されます。
    • Redis バージョン 4.0 以降を使用し、AOF RDB ハイブリッド永続化機能を使用します。
    • AOF の再書き込みまたは保存中にリソースが不足しないように、Redis が占有する最大メモリを合理的に計画します。
    • 単一マシンに複数のインスタンスをデプロイすることは避けてください。
    • 実稼働環境は主にクラスターにデプロイされており、マスターが外部書き込みサービスをより適切に提供できるように、スレーブで永続化機能を有効にすることができます。
    • 災害時バックアップに備えて、バックアップ ファイルはオフサイトのコンピューター室またはクラウド ストレージに自動的にアップロードされる必要があります。

    fork()について

    上記の分析を通じて、RDB スナップショットと AOF の書き換えには fork が必要であることがわかりましたが、これは Redis ブロックにダメージを与える負荷の高い操作です。したがって、Redis メインプロセスの応答に影響を与えないように、ブロッキングを可能な限り減らす必要があります。

    • フォークの頻度を減らします。たとえば、RDB を手動でトリガーしてスナップショットと AOF 書き換えを生成できます。
    • Redis の最大メモリ使用量を制御して、フォークに時間がかかりすぎるのを防ぎます。 ;
    • より高性能のハードウェアを使用する;
    • Linux のメモリ割り当て戦略を適切に構成して、物理メモリ不足によるフォークの失敗を回避します。

    プログラミング関連の知識について詳しくは、プログラミング入門をご覧ください。 !

    以上がRedis の深層学習の永続化原理の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。