Maison > base de données > Redis > Explication détaillée du principe de persistance de l'apprentissage en profondeur Redis

Explication détaillée du principe de persistance de l'apprentissage en profondeur Redis

青灯夜游
Libérer: 2022-03-10 10:54:51
avant
2244 Les gens l'ont consulté

Cet article vous amènera à en savoir plus sur Redis, à analyser la persistance en détail, à présenter son principe de fonctionnement, son processus de persistance, etc. J'espère qu'il vous sera utile !

Cet article présentera le mécanisme de persistance de Redis sous les aspects suivants :

## Écrit avant

Cet article présente les deux méthodes de persistance de Redis en détail dans leur ensemble, y compris les principes de fonctionnement, les processus de persistance et les stratégies de pratiques , et quelques connaissances théoriques derrière eux. L'article précédent n'a présenté que la persistance RDB, mais la persistance Redis est un tout et ne peut pas être introduite séparément, elle a donc été réorganisée. [Recommandations associées : Tutoriel vidéo Redis]

Redis est une base de données en mémoire et toutes les données seront stockées en mémoire par rapport aux bases de données relationnelles traditionnelles telles que MySQL, Oracle et SqlServer, qui enregistrent les données directement sur le disque. disk, Redis L'efficacité de lecture et d'écriture est très élevée. Cependant, il existe également un gros défaut dans la sauvegarde en mémoire. Une fois l'alimentation coupée ou l'ordinateur en panne, tout le contenu de la base de données mémoire sera perdu. Afin de combler cette lacune, Redis fournit la fonction de persistance des données de mémoire dans les fichiers du disque dur et de restauration des données via des fichiers de sauvegarde, ce qui constitue le mécanisme de persistance de Redis.

Redis prend en charge deux méthodes de persistance : l'instantané RDB et l'AOF.

Persistance RDB

Instantané RDB En termes officiels : la solution de persistance RDB est un instantané point à temps généré à partir de votre ensemble de données à un intervalle de temps spécifié. Il enregistre l'instantané de mémoire de tous les objets de données de la base de données Redis à un certain moment dans un fichier binaire compressé, qui peut être utilisé pour la sauvegarde, le transfert et la récupération des données Redis. Jusqu’à présent, il s’agit toujours de la solution officielle de support par défaut.

Comment fonctionne RDB

Étant donné que RDB est un instantané ponctuel de l'ensemble de données dans Redis, comprenons d'abord brièvement comment les objets de données dans Redis sont stockés et organisés en mémoire.

Par défaut, il y a 16 bases de données dans Redis, numérotées de 0 à 15, chaque base de données Redis en utilise une redisDb对象来表示,redisDbUtilisez une table de hachage pour stocker les objets K-V. Pour faciliter la compréhension, j'ai pris l'une des bases de données comme exemple pour dessiner un diagramme schématique de la structure de stockage des données internes de Redis.

Explication détaillée du principe de persistance de lapprentissage en profondeur Redis
Un instantané dans le temps est l'état de chaque objet de données dans chaque base de données dans Redis à un certain moment En supposant d'abord que tous les objets de données ne changeront pas à ce moment, nous pouvons suivre le chiffre. ci-dessus Selon la relation de structure des données, ces objets de données sont lus et écrits dans des fichiers afin d'obtenir la persistance Redis. Ensuite, lorsque Redis redémarre, le contenu de ce fichier est lu selon les règles, puis écrit dans la mémoire Redis pour revenir à l'état de persistance.

Bien sûr, cette prémisse est vraie lorsque notre hypothèse ci-dessus est vraie, sinon nous n'aurions aucun moyen de démarrer face à un ensemble de données qui change tout le temps. Nous savons que le traitement des commandes client dans Redis est un modèle monothread. Si la persistance est traitée comme une commande, l'ensemble de données sera définitivement dans un état statique. De plus, le processus enfant créé par la fonction fork() fournie par le système d'exploitation peut obtenir les mêmes données mémoire que le processus parent, ce qui équivaut à obtenir une copie des données mémoire une fois le fork terminé, que devrait faire le processus enfant ; le processus parent le fait, et le travail de persistance de l'état est confié au processus enfant.

Évidemment, la première situation n'est pas recommandée. Une sauvegarde persistante entraînera l'indisponibilité du service Redis dans un court laps de temps, ce qui est intolérable pour un système à haute haute disponibilité. Par conséquent, la deuxième méthode est la principale méthode pratique de persistance RDB. Étant donné que les données du processus parent continuent de changer après la duplication du processus enfant et que le processus enfant n'est pas synchronisé avec le processus parent, la persistance RDB ne peut pas garantir les performances en temps réel ; la perte de certaines données ; la fréquence de sauvegarde détermine la quantité de données perdues. L'augmentation de la fréquence de sauvegarde signifie que le processus de fork consomme plus de ressources CPU et entraînera également des E/S disque plus importantes.

Processus de persistance

Il existe deux méthodes pour compléter la persistance RDB dans Redis : rdbSave et rdbSaveBackground (dans le fichier de code source rdb.c). Parlons brièvement de la différence entre les deux :

.
  • rdbSave : il est exécuté de manière synchrone et le processus de persistance sera démarré immédiatement après l'appel de la méthode. Étant donné que Redis est un modèle monothread, il sera bloqué pendant le processus de persistance et Redis ne pourra pas fournir de services externes :
  • rdbSaveBackground : elle est exécutée en arrière-plan (de manière asynchrone). le processus de persistance est dans le processus enfant.Après l'exécution (appel de rdbSave), le processus principal continuera à fournir des services

Le déclenchement de la persistance RDB doit être indissociable des deux méthodes ci-dessus, et les méthodes de déclenchement sont divisées en manuel et automatique. Le déclenchement manuel est facile à comprendre.Cela signifie que nous lançons manuellement des instructions de sauvegarde persistantes sur le serveur Redis via le client Redis, puis que le serveur Redis commence à exécuter le processus de persistance. Les instructions ici incluent save et bgsave. Le déclenchement automatique est un processus de persistance que Redis déclenche automatiquement lorsque les conditions prédéfinies sont remplies en fonction de ses propres exigences de fonctionnement. Les scénarios déclenchés automatiquement sont les suivants (extraits de cet article) :

  • save m nin serverCron >. Les règles de configuration sont automatiquement déclenchées ; save m n配置规则自动触发;
  • 从节点全量复制时,主节点发送rdb文件给从节点完成复制操作,主节点会出发bgsave;
  • 执行debug reload命令重新加载redis时;
  • 默认情况下(未开启AOF)执行shutdown命令时,自动执行bgsave;

结合源码及参考文章,我整理了RDB持久化流程来帮助大家有个整体的了解,然后再从一些细节进行说明。
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis
从上图可以知道:

  • 自动触发的RDB持久化是通过rdbSaveBackground以子进程方式执行的持久化策略;
  • 手动触发是以客户端命令方式触发的,包含save和bgsave两个命令,其中save命令是在Redis的命令处理线程以阻塞的方式调用rdbSave方法完成的。

自动触发流程是一个完整的链路,涵盖了rdbSaveBackground、rdbSave等,接下来我以serverCron为例分析一下整个流程。

save规则及检查

serverCron是Redis内的一个周期性函数,每隔100毫秒执行一次,它的其中一项工作就是:根据配置文件中save规则来判断当前需要进行自动持久化流程,如果满足条件则尝试开始持久化。了解一下这部分的实现。

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;                    /* 数据修改次数 */
};
Copier après la connexion

saveparams对应redis.conf下的save规则,save参数是Redis触发自动备份的触发策略,seconds为统计时间(单位:秒), changes为在统计时间内发生写入的次数。save m n的意思是:m秒内有n条写入就触发一次快照,即备份一次。save参数可以配置多组,满足在不同条件的备份要求。如果需要关闭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 ""
Copier après la connexion

serverCron

Lorsque le nœud esclave est entièrement copié, le nœud maître envoie le fichier rdb au nœud esclave pour terminer l'opération de copie, et le nœud maître démarrera bgsave ; La commande est exécutée pour recharger redis ;

Par défaut (AOF n'est pas activé), lors de l'exécution de la commande d'arrêt, bgsave est automatiquement exécuté

En combinant le code source et les articles de référence, j'ai organisé le processus de persistance RDB ; pour aider tout le monde à avoir une compréhension globale, puis à partir de quelques détails.
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis
Comme vous pouvez le constater sur l'image ci-dessus :

  • La persistance RDB déclenchée automatiquement est terminée rdbSaveBackground. Stratégie de persistance exécutée en mode sous-processus ;
  • Le déclenchement manuel est déclenché par la commande client, y compris les commandes save et bgsave, où la commande save est appelée dans le thread de traitement de la commande Redis de manière bloquante rdbSave la méthode est terminée.

Le processus de déclenchement automatique est un lien complet, couvrant rdbSaveBackground, rdbSave, etc. Ensuite, j'utiliserai serverCron comme exemple pour analyser l'ensemble du processus.

règles de sauvegarde et contrôles

🎜serverCron est une fonction périodique de Redis, exécutée toutes les 100 millisecondes. L'une de ses tâches consiste à déterminer le besoin actuel de persistance automatique en fonction des règles de sauvegarde dans le fichier de configuration. , une tentative sera faite pour démarrer la persistance. Découvrez la mise en œuvre de cette partie. 🎜🎜Il y a plusieurs champs liés à la persistance RDB dans redisServer Je les ai extraits du code et les ai regardés en chinois et en anglais : 🎜
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. */
    }
    /* 省略其他逻辑 */
}
Copier après la connexion
🎜saveparams correspond à redis.conf, le paramètre de sauvegarde est la stratégie de déclenchement pour que Redis déclenche la sauvegarde automatique, secondes est le temps statistique (unité : secondes), changes C'est le nombre d'écritures survenues pendant la période statistique. <code>save m n signifie : s'il y a n écritures dans un délai de m secondes, un instantané sera déclenché, c'est-à-dire une sauvegarde. Plusieurs groupes de paramètres de sauvegarde peuvent être configurés pour répondre aux exigences de sauvegarde dans différentes conditions. Si vous devez désactiver la politique de sauvegarde automatique de RDB, vous pouvez utiliser save "". Voici les descriptions de plusieurs configurations : 🎜
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 */
}
Copier après la connexion
🎜serverCron Le code de détection des règles de sauvegarde RDB est le suivant : 🎜
# no-关闭,yes-开启,默认no
appendonly yes
appendfilename appendonly.aof
Copier après la connexion
Copier après la connexion
🎜S'il n'y a pas de persistance RDB en arrière-plan ou de processus de réécriture AOF, serverCron sera basé sur le ci-dessus La configuration et l'état déterminent si des opérations de persistance doivent être effectuées. La base de détermination est de savoir si lastsave et dirty satisfont à l'une des conditions du tableau saveparams. Si une condition est remplie, la méthode rdbSaveBackground est appelée pour exécuter le processus de persistance asynchrone. 🎜🎜rdbSaveBackground🎜🎜rdbSaveBackground est une méthode auxiliaire pour la persistance RDB. Sa tâche principale est de créer le processus enfant, puis il existe deux logiques d'exécution différentes en fonction de l'appelant (processus parent ou processus enfant). 🎜🎜🎜Si l'appelant est le processus parent, déboursez le processus enfant, enregistrez les informations du processus enfant et revenez directement. 🎜🎜Si l'appelant est un processus enfant, appelez rdbSave pour exécuter la logique de persistance RDB et quittez le processus enfant une fois la persistance terminée. 🎜🎜
set number 0
incr number
incr number
incr number
incr number
incr number
Copier après la connexion
Copier après la connexion
🎜rdbSave est une méthode qui effectue véritablement de la persistance. Elle implique un grand nombre d'opérations d'E/S et de calcul pendant l'exécution, ce qui prend du temps et consomme beaucoup de CPU dans le modèle monothread de Redis. le processus de persistance continuera à occuper les ressources du thread, ce qui empêchera Redis de fournir d'autres services. Afin de résoudre ce problème, Redis supprime le processus enfant dans rdbSaveBackground, et le processus enfant termine le travail de persistance, évitant ainsi d'occuper trop de ressources du processus parent. 🎜🎜Il convient de noter que si la mémoire occupée par le processus parent est trop grande, le processus fork prendra beaucoup de temps et le processus parent ne pourra pas fournir de services au monde extérieur. De plus, l'utilisation de la mémoire de l'ordinateur. doit être considéré de manière globale. Après avoir bifurqué le processus enfant, il occupera le double des ressources mémoire, vous devez vous assurer que la mémoire est suffisante. Vérifiez l'option last_fork_usec via la commande info stats pour obtenir le temps nécessaire à la dernière opération de fork. 🎜

rdbSave

Redis的rdbSave函数是真正进行RDB持久化的函数,流程、细节贼多,整体流程可以总结为:创建并打开临时文件、Redis内存数据写入临时文件、临时文件写入磁盘、临时文件重命名为正式RDB文件、更新持久化状态信息(dirty、lastsave)。其中“Redis内存数据写入临时文件”最为核心和复杂,写入过程直接体现了RDB文件的文件格式,本着一图胜千言的理念,我按照源码流程绘制了下图。
Explication détaillée du principe de persistance de lapprentissage en profondeur 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
Copier après la connexion
Copier après la connexion

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
Copier après la connexion
Copier après la connexion

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

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

持久化流程

从流程上来看,AOF的工作原理可以概括为几个步骤:命令追加(append)、文件写入与同步(fsync)、文件重写(rewrite)、重启加载(load),接下来依次了解每个步骤的细节及背后的设计哲学。
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis

Command append

Lorsque la fonction de persistance AOF est activée, après que Redis ait exécuté une commande d'écriture, elle ajoutera la commande d'écriture exécutée au format de protocole (c'est-à-dire RESP, le protocole de communication pour l'interaction entre le client Redis et le serveur) Ajouté à la fin du tampon AOF géré par le serveur Redis. Il n'y a qu'une seule opération d'ajout à thread pour les fichiers AOF, et il n'y a pas d'opérations complexes telles que la recherche. Il n'y a aucun risque d'endommagement des fichiers même en cas de panne de courant ou de temps d'arrêt. De plus, l'utilisation de protocoles texte présente de nombreux avantages :

  • Les protocoles texte ont une bonne compatibilité ;
  • Les protocoles texte sont des commandes de demande client et ne nécessitent pas de traitement secondaire, ce qui permet d'économiser des frais de traitement pendant le stockage et le chargement ; lisible et pratique pour la visualisation, la modification et d’autres traitements.
  • Le type de tampon AOF est une structure de données sds conçue indépendamment par Redis qui utilisera différentes méthodes selon le type de commande (catAppendOnlyGenericCommand, catAppendOnlyExpireAtCommand<.> etc.) traite le contenu de la commande et enfin l'écrit dans le tampon. </.>

Il est à noter que si la réécriture AOF est en cours lorsque les commandes sont ajoutées, ces commandes seront également ajoutées au tampon de réécriture (aof_rewrite_buffer). sds,Redis会根据命令的类型采用不同的方法(catAppendOnlyGenericCommandcatAppendOnlyExpireAtCommand等)对命令内容进行处理,最后写入缓冲区。

需要注意的是:如果命令追加时正在进行AOF重写,这些命令还会追加到重写缓冲区(aof_rewrite_buffer)。

文件写入与同步

AOF文件的写入与同步离不开操作系统的支持,开始介绍之前,我们需要补充一下Linux I/O缓冲区相关知识。硬盘I/O性能较差,文件读写速度远远比不上CPU的处理速度,如果每次文件写入都等待数据写入硬盘,会整体拉低操作系统的性能。为了解决这个问题,操作系统提供了延迟写(delayed write)机制来提高硬盘的I/O性能。
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis

传统的UNIX实现在内核中设有缓冲区高速缓存或页面高速缓存,大多数磁盘I/O都通过缓冲进行。 当将数据写入文件时,内核通常先将该数据复制到其中一个缓冲区中,如果该缓冲区尚未写满,则并不将其排入输出队列,而是等待其写满或者当内核需要重用该缓冲区以便存放其他磁盘块数据时, 再将该缓冲排入到输出队列,然后待其到达队首时,才进行实际的I/O操作。这种输出方式就被称为延迟写。

延迟写减少了磁盘读写次数,但是却降低了文件内容的更新速度,使得欲写到文件中的数据在一段时间内并没有写到磁盘上。当系统发生故障时,这种延迟可能造成文件更新内容的丢失。为了保证磁盘上实际文件系统与缓冲区高速缓存中内容的一致性,UNIX系统提供了sync、fsync和fdatasync三个函数为强制写入硬盘提供支持。

Redis每次事件轮训结束前(beforeSleep)都会调用函数flushAppendOnlyFileflushAppendOnlyFile会把AOF缓冲区(aof_buf)中的数据写入内核缓冲区,并且根据appendfsync配置来决定采用何种策略把内核缓冲区中的数据写入磁盘,即调用fsync()。该配置有三个可选项alwaysnoeverysec,具体说明如下:

  • always:每次都调用fsync(),是安全性最高、性能最差的一种策略。
  • no:不会调用fsync()。性能最好,安全性最差。
  • everysec:仅在满足同步条件时调用fsync()。这是官方建议的同步策略,也是默认配置,做到兼顾性能和数据安全性,理论上只有在系统突然宕机的情况下丢失1秒的数据。

注意:上面介绍的策略受配置项no-appendfsync-on-rewrite的影响,它的作用是告知Redis:AOF文件重写期间是否禁止调用fsync(),默认是no。

如果appendfsync设置为alwayseverysec,后台正在进行的BGSAVE或者BGREWRITEAOF消耗过多的磁盘I/O,在某些Linux系统配置下,Redis对fsync()的调用可能阻塞很长时间。然而这个问题还没有修复,因为即使是在不同的线程中执行fsync(),同步写入操作也会被阻塞。

为了缓解此问题,可以使用该选项,以防止在进行BGSAVEBGREWRITEAOF

Écriture et synchronisation de fichiers🎜🎜L'écriture et la synchronisation des fichiers AOF ne peuvent être séparées de la prise en charge du système d'exploitation Avant de commencer à présenter, nous devons ajouter quelques connaissances sur les tampons d'E/S Linux. Les performances d'E/S du disque dur sont médiocres et la vitesse de lecture et d'écriture des fichiers est bien inférieure à la vitesse de traitement du processeur. Si vous attendez que les données soient écrites sur le disque dur à chaque fois que vous écrivez un fichier, cela se produit. réduira les performances globales du système d’exploitation. Afin de résoudre ce problème, le système d'exploitation fournit un mécanisme d'écriture différée pour améliorer les performances d'E/S du disque dur.
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis🎜
L'implémentation UNIX traditionnelle a un cache de tampon ou un cache de pages dans le noyau. La plupart des disques I/ O est effectué via la mise en mémoire tampon. Lors de l'écriture de données dans un fichier, le noyau copie généralement d'abord les données dans l'un des tampons. Si le tampon n'est pas encore plein, il ne le met pas en file d'attente dans la file d'attente de sortie, mais attend qu'il se remplisse ou lorsque le noyau en a besoin. Lorsque le tampon est réutilisé pour stocker d'autres données de bloc de disque, le tampon est mis en file d'attente dans la file d'attente de sortie, puis l'opération d'E/S réelle n'est effectuée que lorsqu'elle atteint la tête de la file d'attente. Cette méthode de sortie est appelée écriture différée.
🎜L'écriture différée réduit le nombre de lectures et d'écritures sur le disque, mais elle réduit également la vitesse de mise à jour du contenu du fichier, de sorte que les données à écrire dans le fichier ne soient pas écrites sur le disque pendant un certain temps. Lorsqu'une panne du système se produit, ce délai peut entraîner la perte des mises à jour des fichiers. Afin de garantir la cohérence du système de fichiers réel sur le disque et du contenu du cache tampon, le système UNIX fournit trois fonctions : sync, fsync et fdatasync pour prendre en charge l'écriture forcée sur le disque dur. 🎜🎜Redis appellera la fonction flushAppendOnlyFile avant la fin de chaque rotation d'événement (beforeSleep), et flushAppendOnlyFile appellera le tampon AOF (aof_buf ) est écrit dans le tampon du noyau et la stratégie utilisée pour écrire les données du tampon du noyau sur le disque est déterminée en fonction de la configuration <code>appendfsync, c'est-à-dire en appelant fsync( ). Cette configuration comporte trois options : <code>always, no et everysec. Les détails sont les suivants : 🎜🎜🎜always : appelez fsync. à chaque fois (), est la stratégie avec la sécurité la plus élevée et les pires performances. 🎜🎜non : fsync() ne sera pas appelé. Meilleures performances, pire sécurité. 🎜🎜everysec : Appelez fsync() uniquement lorsque les conditions de synchronisation sont remplies. Il s'agit de la stratégie de synchronisation officiellement recommandée et c'est également la configuration par défaut. Elle prend en compte à la fois les performances et la sécurité des données. En théorie, seulement 1 seconde de données sera perdue en cas d'arrêt soudain du système. 🎜🎜🎜Remarque : La stratégie présentée ci-dessus est affectée par l'élément de configuration no-appendfsync-on-rewrite. Sa fonction est d'informer Redis s'il doit interdire l'appel de fsync() lors de la réécriture du fichier AOF. , la valeur par défaut est non. 🎜🎜Si appendfsync est défini sur always ou everysec, BGSAVE ou est en cours traité en arrière-plan >BGREWRITEAOF consomme trop d'E/S disque Sous certaines configurations système Linux, l'appel de Redis à fsync() peut se bloquer pendant une longue période. Cependant, ce problème n'a pas encore été résolu, car l'opération d'écriture synchrone sera bloquée même si fsync() est exécuté dans un thread différent. 🎜🎜Pour atténuer ce problème, cette option peut être utilisée pour empêcher fsync() d'être appelé dans le processus principal lors de l'exécution de BGSAVE ou BGREWRITEAOF. 🎜
  • 设置为yes意味着,如果子进程正在进行BGSAVEBGREWRITEAOF,AOF的持久化能力就与appendfsync设置为no有着相同的效果。最糟糕的情况下,这可能会导致30秒的缓存数据丢失。
  • 如果你的系统有上面描述的延迟问题,就把这个选项设置为yes,否则保持为no

文件重写

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

为了解决AOF文件体积问题,Redis引入了AOF文件重写功能,它会根据Redis内数据对象的最新状态生成新的AOF文件,新旧文件对应的数据状态一致,但是新文件会具有较小的体积。重写既减少了AOF文件对磁盘空间的占用,又可以提高Redis重启时数据恢复的速度。还是下面这个例子,旧文件中的6条命令等同于新文件中的1条命令,压缩效果显而易见。
Explication détaillée du principe de persistance de lapprentissage en profondeur 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
Copier après la connexion

手动触发与自动触发的代码如下,同样在周期性方法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();
            }
        }
    }
    /**/
}
Copier après la connexion

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)流程图:
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis
结合上图,总结一下AOF文件重写的流程:

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

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

    • Modifiez le nom du processus actuel, créez le fichier temporaire requis pour la réécriture et appelez la fonction rewriteAppendOnlyFile
    • Selon la configuration aof-use-rdb-preamble, écrivez la première moitié en RDB ou Mode AOF et synchronisation avec le disque dur ; aof-use-rdb-preamble配置,以RDB或AOF方式写入前半部分,并同步至硬盘;
    • 从父进程接收增量AOF命令,以AOF方式写入后半部分,并同步至硬盘;
    • 重命名AOF文件,子进程退出。

数据加载

Redis启动后通过loadDataFromDisk函数执行数据加载工作。这里需要注意,虽然持久化方式可以选择AOF、RDB或者两者兼用,但是数据加载时必须做出选择,两种方式各自加载一遍就乱套了。

理论上,AOF持久化比RDB具有更好的实时性,当开启了AOF持久化方式,Redis在数据加载时优先考虑AOF方式。而且,Redis 4.0版本后AOF支持了混合持久化,加载AOF文件需要考虑版本兼容性。Redis数据加载流程如下图所示:
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis
在AOF方式下,开启混合持久化机制生成的文件是“RDB头+AOF尾”,未开启时生成的文件全部为AOF格式。考虑两种文件格式的兼容性,如果Redis发现AOF文件为RDB头,会使用RDB数据加载的方法读取并恢复前半部分;然后再使用AOF方式读取并恢复后半部分。由于AOF格式存储的数据为RESP协议命令,Redis采用伪客户端执行命令的方式来恢复数据。

如果在AOF命令追加过程中发生宕机,由于延迟写的技术特点,AOF的RESP命令可能不完整(被截断)。遇到这种情况时,Redis会按照配置项aof-load-truncated

Recevez la commande AOF incrémentielle du processus parent, écrivez la seconde moitié en mode AOF et synchronisez avec le disque dur ;
  • Renommez le fichier AOF et le processus enfant se termine.

Chargement des données

Après le démarrage de Redis, le travail de chargement des données est effectué via la fonction loadDataFromDisk. Il convient de noter ici que même si la méthode de persistance peut être AOF, RDB ou les deux, un choix doit être fait lors du chargement des données. Le chargement des deux méthodes séparément provoquera le chaos.

Théoriquement, la persistance AOF a de meilleures performances en temps réel que RDB Lorsque la persistance AOF est activée, Redis donne la priorité à AOF lors du chargement des données. De plus, après la version Redis 4.0, AOF prend en charge la persistance hybride et la compatibilité des versions doit être prise en compte lors du chargement des fichiers AOF. Le processus de chargement des données Redis est illustré dans la figure ci-dessous :
Explication détaillée du principe de persistance de lapprentissage en profondeur Redis
En mode AOF, activez l'hybride mécanisme de persistance Le fichier généré est "En-tête RDB + queue AOF". Lorsqu'il n'est pas activé, tous les fichiers générés sont au format AOF. Compte tenu de la compatibilité des deux formats de fichier, si Redis constate que le fichier AOF est un en-tête RDB, il utilisera la méthode de chargement de données RDB pour lire et restaurer la première moitié, puis utilisera la méthode AOF pour lire et restaurer la seconde moitié ; . Étant donné que les données stockées au format AOF sont des commandes du protocole RESP, Redis utilise un pseudo client pour exécuter des commandes permettant de récupérer les données.

🎜Si un temps d'arrêt survient lors du processus d'ajout de la commande AOF, la commande RESP de l'AOF peut être incomplète (tronquée) en raison des caractéristiques techniques d'écriture retardée. Lorsqu'il rencontre cette situation, Redis exécutera différentes stratégies de traitement en fonction de l'élément de configuration aof-load-truncated. Cette configuration indique à Redis de lire le fichier aof au démarrage, et que faire si le fichier s'avère tronqué (incomplet) : 🎜🎜🎜oui : Chargez autant de données que possible et avertissez l'utilisateur sous forme de log ; 🎜🎜non : Ensuite, il plante avec une erreur système et il est interdit de démarrer. L'utilisateur doit réparer le fichier puis redémarrer. 🎜🎜🎜Résumé🎜🎜Redis propose deux options de persistance : RDB prend en charge la génération d'instantanés ponctuels pour l'ensemble de données à des intervalles pratiques spécifiques ; AOF conserve chaque instruction d'écriture reçue par le serveur Redis dans le journal. redémarrer. Le format du journal est le protocole RESP et seule l'opération d'ajout est effectuée sur le fichier journal, il n'y a donc aucun risque de dommage. Et lorsque le fichier AOF est trop volumineux, le fichier compressé peut être automatiquement réécrit. 🎜🎜Bien sûr, vous pouvez également désactiver la fonction de persistance de Redis si vous n'avez pas besoin de conserver les données, mais ce n'est pas le cas dans la plupart des cas. En fait, on peut parfois utiliser RDB et AOF en même temps. Le plus important est de comprendre la différence entre les deux afin de les utiliser raisonnablement. 🎜

RDB vs AOF

Avantages RDB

  • RDB est un fichier binaire compact et compressé, qui représente un instantané des données Redis à un moment donné. Il est très approprié pour la sauvegarde, la réplication complète et d'autres scénarios.
  • RDB est très convivial pour la reprise après sinistre et la migration des données. Les fichiers RDB peuvent être déplacés vers n'importe quel endroit nécessaire et rechargés.
  • RDB est un instantané de mémoire des données Redis, avec une récupération de données plus rapide et des performances supérieures à celles de la relecture des commandes AOF.

Inconvénients RDB

  • La méthode RDB ne peut pas atteindre une persistance en temps réel ou de deuxième niveau. Étant donné que le processus de persistance est terminé par le processus enfant après le fork du processus enfant, la mémoire du processus enfant n'est qu'un instantané des données du processus parent au moment de l'opération de fork, le processus parent continue. pour servir le monde extérieur, et les données internes changent tout le temps. Les données ne sont plus mises à jour et il y a toujours des différences entre les deux, de sorte que les performances en temps réel ne peuvent pas être obtenues.
  • L'opération fork pendant le processus de persistance RDB doublera l'utilisation de la mémoire, et plus le processus parent possède de données, plus le processus fork sera long.
  • Une concurrence élevée des requêtes Redis peut fréquemment heurter la règle de sauvegarde, ce qui rend la fréquence des opérations de fork et des sauvegardes persistantes incontrôlable ;
  • Les fichiers RDB ont des exigences en matière de format de fichier, et différentes versions de Redis ajusteront le format de fichier, et certaines anciennes les versions sont incompatibles Problème avec la nouvelle version.

Avantages AOF

  • La persistance AOF a de meilleures performances en temps réel, nous pouvons choisir trois méthodes différentes (appendfsync) : non, chaque seconde, toujours, chaque seconde car la stratégie par défaut a les meilleures performances, extrême Dans ce cas, une seconde de données peut être perdue.
  • Les fichiers AOF n'ont que des opérations d'ajout, pas de recherche compliquée ni d'autres opérations de fichiers, et aucun risque de dommage. Même si les dernières données écrites sont tronquées, elles peuvent être facilement réparées à l'aide de l'outil redis-check-aof
  • Lorsque le fichier AOF devient plus volumineux, Redis peut le réécrire automatiquement en arrière-plan ; Pendant le processus de réécriture, l'ancien fichier continuera à être écrit. Une fois la réécriture terminée, le nouveau fichier deviendra plus petit et la commande incrémentielle pendant le processus de réécriture sera également ajoutée au nouveau fichier.
  • Le fichier AOF contient toutes les commandes d'opération pour les données dans Redis d'une manière facile à comprendre et à analyser. Même si toutes les données sont accidentellement effacées par erreur, tant que le fichier AOF n'est pas réécrit, nous pouvons récupérer toutes les données en supprimant la dernière commande.
  • AOF prend déjà en charge la persistance hybride, la taille du fichier peut être contrôlée efficacement et l'efficacité du chargement des données est améliorée.
Inconvénients d'AOF

    Pour la même collecte de données, les fichiers AOF sont généralement plus gros que les fichiers RDB ;
  • Dans le cadre d'une stratégie fsync spécifique, AOF sera légèrement plus lent que RDB ; D'une manière générale, les performances de fsync_every_second sont toujours très élevées et les performances de fsync_no sont comparables à celles de RDB. Mais sous une énorme pression d’écriture, RDB peut offrir la plus grande garantie de faible latence.
  • Sur AOF, Redis a rencontré un jour des bugs rares qu'il est presque impossible de rencontrer sur RDB. Certaines instructions spéciales (telles que BRPOPLPUSH) rendent les données rechargées incohérentes avec celles d'avant la persistance, une fois testées par les responsables de Redis dans les mêmes conditions, mais le problème n'a pas pu être reproduit.
Suggestions d'utilisation

Après avoir compris les principes de fonctionnement, les processus d'exécution, les avantages et les inconvénients des deux méthodes de persistance RDB et AOF, réfléchissons à la manière de peser le pour et le contre dans des scénarios réels et d'utiliser les deux méthodes de persistance de manière raisonnable. Si vous utilisez Redis uniquement comme outil de mise en cache, toutes les données peuvent être reconstruites sur la base de la base de données persistante. Vous pouvez désactiver la fonction de persistance et effectuer des travaux de protection tels que le préchauffage, la pénétration du cache, la panne et l'avalanche.

Dans des circonstances normales, Redis assumera davantage de tâches, telles que les verrous distribués, les classements, les centres d'enregistrement, etc. La fonction de persistance jouera un rôle plus important dans la reprise après sinistre et la migration des données. Il est recommandé de suivre plusieurs principes :

    Ne pas utiliser Redis comme base de données Dans la mesure du possible, toutes les données peuvent être reconstruites automatiquement par le service applicatif.
  • Utilisez Redis version 4.0 ou supérieure et utilisez la fonction de persistance hybride AOF+RDB.
  • Planifiez correctement la mémoire maximale occupée par Redis pour éviter des ressources insuffisantes lors de la réécriture ou de la sauvegarde AOF.
  • Évitez de déployer plusieurs instances sur une seule machine.
  • La plupart des environnements de production sont déployés en clusters. La capacité de persistance peut être activée sur l'esclave, permettant au maître de mieux fournir des services d'écriture externes.
  • Les fichiers de sauvegarde doivent être automatiquement téléchargés vers des salles informatiques hors site ou vers un stockage cloud pour préparer la sauvegarde en cas de sinistre.
À propos de fork()

Grâce à l'analyse ci-dessus, nous savons tous que les instantanés RDB et la réécriture AOF nécessitent un fork. Il s'agit d'une opération lourde qui entraînera un blocage de Redis. Par conséquent, afin de ne pas affecter la réponse du processus principal Redis, nous devons réduire le blocage autant que possible.

    Réduisez la fréquence de fork, par exemple, vous pouvez déclencher manuellement RDB pour générer des instantanés et une réécriture AOF ;
  • Contrôlez l'utilisation maximale de la mémoire de Redis pour éviter que fork ne prenne trop de temps ;
  • Utilisez du matériel plus performant ;
  • La stratégie d'allocation de mémoire de Linux correctement configurée évite les échecs de fork dus à une mémoire physique insuffisante.
  • Pour plus de connaissances sur la programmation, veuillez visiter :
Introduction à la programmation

 ! !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal