Compression des données dans Apache Ignite. L'expérience de Sber

Compression des données dans Apache Ignite. L'expérience de SberLorsque l'on travaille avec de gros volumes de données, le problème du manque d'espace disque peut parfois survenir. Une façon de résoudre ce problème est la compression, grâce à laquelle, sur le même équipement, vous pouvez vous permettre d'augmenter les volumes de stockage. Dans cet article, nous verrons comment fonctionne la compression des données dans Apache Ignite. Cet article décrira uniquement les méthodes de compression de disque implémentées dans le produit. Les autres méthodes de compression de données (sur le réseau, en mémoire), qu'elles soient mises en œuvre ou non, resteront hors du champ d'application.

Ainsi, avec le mode persistance activé, suite aux modifications des données dans les caches, Ignite commence à écrire sur le disque :

  1. Contenu des caches
  2. Journal d'écriture anticipée (ci-après simplement WAL)

Il existe depuis un certain temps déjà un mécanisme de compression WAL, appelé compactage WAL. Apache Ignite 2.8, récemment publié, a introduit deux mécanismes supplémentaires qui vous permettent de compresser des données sur le disque : la compression de page disque pour compresser le contenu des caches et la compression d'instantanés de page WAL pour compresser certaines entrées WAL. Plus de détails sur ces trois mécanismes ci-dessous.

Compression des pages disque

Comment ça marche

Tout d’abord, examinons très brièvement comment Ignite stocke les données. La mémoire de page est utilisée pour le stockage. La taille de la page est définie au début du nœud et ne peut pas être modifiée ultérieurement ; elle doit également être une puissance de deux et un multiple de la taille du bloc du système de fichiers. Les pages sont chargées dans la RAM à partir du disque selon les besoins ; la taille des données sur le disque peut dépasser la quantité de RAM allouée. S'il n'y a pas assez d'espace dans la RAM pour charger une page à partir du disque, les anciennes pages qui ne sont plus utilisées seront expulsées de la RAM.

Les données sont stockées sur le disque sous la forme suivante : un fichier distinct est créé pour chaque partition de chaque groupe de cache ; dans ce fichier, les pages apparaissent les unes après les autres dans l'ordre d'index croissant. L'identifiant de page complète contient l'identifiant du groupe de cache, le numéro de partition et l'index de page dans le fichier. Ainsi, en utilisant l'identifiant de page complète, nous pouvons déterminer de manière unique le fichier et le décalage dans le fichier pour chaque page. Vous pouvez en savoir plus sur la mémoire de pagination dans l'article Apache Ignite Wiki : Ignite Persistent Store - sous le capot.

Le mécanisme de compression des pages du disque, comme son nom l'indique, fonctionne au niveau de la page. Lorsque ce mécanisme est activé, les données dans la RAM sont traitées telles quelles, sans aucune compression, mais lorsque les pages sont enregistrées de la RAM sur le disque, elles sont compressées.

Mais compresser chaque page individuellement n'est pas une solution au problème : vous devez d'une manière ou d'une autre réduire la taille des fichiers de données résultants. Si la taille de la page n'est plus fixe, nous ne pouvons plus écrire les pages dans le fichier les unes après les autres, car cela peut créer un certain nombre de problèmes :

  • A l'aide de l'index de la page, nous ne pourrons pas calculer le décalage par lequel elle se situe dans le fichier.
  • On ne sait pas quoi faire avec les pages qui ne se trouvent pas à la fin du fichier et qui changent de taille. Si la taille de la page diminue, l'espace libéré disparaît. Si la taille de la page augmente, vous devez rechercher un nouvel emplacement dans le fichier.
  • Si une page se déplace d'un nombre d'octets qui n'est pas un multiple de la taille du bloc du système de fichiers, sa lecture ou son écriture nécessitera de toucher un bloc supplémentaire du système de fichiers, ce qui peut entraîner une dégradation des performances.

Pour éviter de résoudre ces problèmes à son propre niveau, la compression des pages disque dans Apache Ignite utilise un mécanisme de système de fichiers appelé fichiers fragmentés. Un fichier clairsemé est un fichier dans lequel certaines régions remplies de zéros peuvent être marquées comme des « trous ». Dans ce cas, aucun bloc du système de fichiers ne sera alloué pour stocker ces trous, ce qui entraînera des économies d'espace disque.

Il est logique que pour libérer un bloc du système de fichiers, la taille du trou doit être supérieure ou égale au bloc du système de fichiers, ce qui impose une limitation supplémentaire sur la taille de la page et d'Apache Ignite : pour que la compression ait un effet, la taille de la page doit être strictement supérieure à la taille du bloc du système de fichiers. Si la taille de la page est égale à la taille du bloc, alors nous ne pourrons jamais libérer un seul bloc, car pour libérer un seul bloc, la page compressée doit occuper 0 octet. Si la taille de la page est égale à la taille de 2 ou 4 blocs, nous pourrons déjà libérer au moins un bloc si notre page est compressée à au moins 50 % ou 75 %, respectivement.

Ainsi, la description finale du fonctionnement du mécanisme : Lors de l'écriture d'une page sur le disque, une tentative est faite pour compresser la page. Si la taille de la page compressée permet de libérer un ou plusieurs blocs du système de fichiers, alors la page est écrite sous forme compressée, et un « trou » est fait à la place des blocs libérés (un appel système est exécuté fallocate() avec le drapeau perforé). Si la taille de la page compressée ne permet pas de libérer les blocs, la page est enregistrée telle quelle, non compressée. Tous les décalages de page sont calculés de la même manière que sans compression, en multipliant l'index de la page par la taille de la page. Aucun déplacement de pages n’est requis par vous-même. Les décalages de page, tout comme sans compression, se situent aux limites des blocs du système de fichiers.

Compression des données dans Apache Ignite. L'expérience de Sber

Dans l'implémentation actuelle, Ignite ne peut fonctionner qu'avec des fichiers fragmentés sous le système d'exploitation Linux ; par conséquent, la compression des pages disque ne peut être activée que lors de l'utilisation d'Ignite sur ce système d'exploitation.

Algorithmes de compression pouvant être utilisés pour la compression des pages disque : ZSTD, LZ4, Snappy. De plus, il existe un mode de fonctionnement (SKIP_GARBAGE), dans lequel seul l'espace inutilisé de la page est supprimé sans appliquer de compression sur les données restantes, ce qui réduit la charge sur le processeur par rapport aux algorithmes répertoriés précédemment.

Impact sur les performances

Malheureusement, je n'ai pas effectué de mesures réelles des performances sur des stands réels, car nous ne prévoyons pas d'utiliser ce mécanisme en production, mais nous pouvons théoriquement spéculer où nous perdrons et où nous gagnerons.

Pour ce faire, nous devons nous rappeler comment les pages sont lues et écrites lors de leur accès :

  • Lors de l'exécution d'une opération de lecture, elle est d'abord recherchée dans la RAM ; si la recherche échoue, la page est chargée dans la RAM à partir du disque par le même thread qui effectue la lecture.
  • Lorsqu'une opération d'écriture est effectuée, la page dans la RAM est marquée comme sale, mais la page n'est pas physiquement enregistrée sur le disque immédiatement par le thread effectuant l'écriture. Toutes les pages sales sont enregistrées sur le disque ultérieurement au cours du processus de point de contrôle dans des threads séparés.

L'impact sur les opérations de lecture est donc :

  • Positif (E/S disque), en raison d'une diminution du nombre de blocs du système de fichiers lus.
  • Négatif (CPU), en raison de la charge supplémentaire requise par le système d'exploitation pour travailler avec des fichiers clairsemés. Il est également possible que des opérations d'E/S supplémentaires apparaissent implicitement ici pour enregistrer une structure de fichiers fragmentés plus complexe (malheureusement, je ne connais pas tous les détails du fonctionnement des fichiers fragmentés).
  • Négatif (CPU), dû à la nécessité de décompresser les pages.
  • Il n’y a aucun impact sur les opérations d’écriture.
  • Impact sur le processus de point de contrôle (tout ici est similaire aux opérations de lecture) :
  • Positif (E/S disque), en raison d'une diminution du nombre de blocs du système de fichiers écrits.
  • Négatif (CPU, éventuellement disque IO), en raison du travail avec des fichiers clairsemés.
  • Négatif (CPU), en raison de la nécessité de compresser les pages.

De quel côté de la balance fera-t-il pencher la balance ? Tout dépend beaucoup de l'environnement, mais j'ai tendance à croire que la compression des pages disque entraînera très probablement une dégradation des performances sur la plupart des systèmes. De plus, des tests sur d'autres SGBD utilisant une approche similaire avec des fichiers clairsemés montrent une baisse des performances lorsque la compression est activée.

Comment activer et configurer

Comme mentionné ci-dessus, la version minimale d'Apache Ignite prenant en charge la compression des pages disque est 2.8 et seul le système d'exploitation Linux est pris en charge. Activez et configurez comme suit :

  • Il doit y avoir un module ignite-compression dans le chemin de classe. Par défaut, il se trouve dans la distribution Apache Ignite dans le répertoire libs/optional et n'est pas inclus dans le chemin de classe. Vous pouvez simplement déplacer le répertoire d'un niveau vers libs, puis lorsque vous l'exécuterez via ignite.sh, il sera automatiquement activé.
  • La persistance doit être activée (Activée via DataRegionConfiguration.setPersistenceEnabled(true)).
  • La taille de la page doit être supérieure à la taille du bloc du système de fichiers (vous pouvez la définir en utilisant DataStorageConfiguration.setPageSize() ).
  • Pour chaque cache dont les données doivent être compressées, vous devez configurer la méthode de compression et (éventuellement) le niveau de compression (méthodes CacheConfiguration.setDiskPageCompression() , CacheConfiguration.setDiskPageCompressionLevel()).

Compactage des WAL

Comment ça marche

Qu'est-ce que WAL et pourquoi est-il nécessaire ? Très brièvement : il s'agit d'un journal qui contient tous les événements qui modifient finalement le stockage des pages. Il est nécessaire avant tout pour pouvoir récupérer en cas de chute. Toute opération, avant de donner le contrôle à l'utilisateur, doit d'abord enregistrer un événement dans WAL, afin qu'en cas d'échec, il puisse être relu dans le journal et restaurer toutes les opérations pour lesquelles l'utilisateur a reçu une réponse réussie, même si ces opérations n'a pas eu le temps de se refléter dans le stockage des pages sur le disque (déjà ci-dessus, il a été décrit que l'écriture réelle dans le magasin de pages se fait dans un processus appelé "checkpointing" avec un certain retard par des threads séparés).

Les entrées dans WAL sont divisées en logiques et physiques. Les booléens sont les clés et les valeurs elles-mêmes. Physique : reflète les modifications apportées aux pages dans le magasin de pages. Bien que les enregistrements logiques puissent être utiles dans d'autres cas, les enregistrements physiques ne sont nécessaires que pour la récupération en cas de panne et les enregistrements ne sont nécessaires que depuis le dernier point de contrôle réussi. Ici, nous n'entrerons pas dans les détails et n'expliquerons pas pourquoi cela fonctionne de cette façon, mais les personnes intéressées peuvent se référer à l'article déjà mentionné sur le wiki Apache Ignite : Ignite Persistent Store - sous le capot.

Il existe souvent plusieurs enregistrements physiques par enregistrement logique. Autrement dit, une opération de mise dans le cache affecte plusieurs pages de la mémoire de pages (une page avec les données elles-mêmes, des pages avec des index, des pages avec des listes libres). Lors de certains tests synthétiques, j'ai constaté que les enregistrements physiques occupaient jusqu'à 90 % du fichier WAL. Cependant, ils sont nécessaires pendant une durée très courte (par défaut, l'intervalle entre les points de contrôle est de 3 minutes). Il serait logique de se débarrasser de ces données après avoir perdu leur pertinence. C'est exactement ce que fait le mécanisme de compactage WAL : il supprime les enregistrements physiques et compresse les enregistrements logiques restants à l'aide de zip, tandis que la taille du fichier est réduite de manière très significative (parfois des dizaines de fois).

Physiquement, WAL est constitué de plusieurs segments (10 par défaut) d'une taille fixe (64 Mo par défaut), qui sont écrasés de manière circulaire. Dès que le segment actuel est rempli, le segment suivant est attribué comme segment actuel et le segment rempli est copié dans l'archive par un thread séparé. Le compactage WAL fonctionne déjà avec les segments d'archives. De plus, en tant que thread distinct, il surveille l'exécution du point de contrôle et commence la compression dans les segments d'archives pour lesquels les enregistrements physiques ne sont plus nécessaires.

Compression des données dans Apache Ignite. L'expérience de Sber

Impact sur les performances

Étant donné que le compactage des WAL s'exécute comme un thread distinct, il ne devrait y avoir aucun impact direct sur les opérations en cours. Mais cela impose toujours une charge de fond supplémentaire sur le processeur (compression) et le disque (lecture de chaque segment WAL de l'archive et écriture des segments compressés), donc si le système fonctionne à sa capacité maximale, cela entraînera également une dégradation des performances.

Comment activer et configurer

Vous pouvez activer le compactage WAL en utilisant la propriété WalCompactionEnabled в DataStorageConfiguration (DataStorageConfiguration.setWalCompactionEnabled(true)). De plus, à l'aide de la méthode DataStorageConfiguration.setWalCompactionLevel(), vous pouvez définir le niveau de compression si vous n'êtes pas satisfait de la valeur par défaut (BEST_SPEED).

Compression des instantanés de pages WAL

Comment ça marche

Nous avons déjà découvert que dans WAL, les enregistrements sont divisés en logiques et physiques. Pour chaque modification apportée à chaque page, un enregistrement WAL physique est généré dans la mémoire des pages. Les enregistrements physiques, à leur tour, sont également divisés en 2 sous-types : enregistrement d'instantané de page et enregistrement delta. Chaque fois que nous modifions quelque chose sur une page et la transférons d'un état propre à un état sale, une copie complète de cette page est stockée dans WAL (enregistrement instantané de page). Même si nous ne modifions qu'un seul octet dans WAL, l'enregistrement sera légèrement plus grand que la taille de la page. Si nous modifions quelque chose sur une page déjà sale, un enregistrement delta est formé dans WAL, qui reflète uniquement les changements par rapport à l'état précédent de la page, mais pas la page entière. Étant donné que la réinitialisation de l'état des pages de sale à propre est effectuée pendant le processus de point de contrôle, immédiatement après le début du point de contrôle, presque tous les enregistrements physiques seront constitués uniquement d'instantanés de pages (puisque toutes les pages immédiatement après le début du point de contrôle sont propres) , puis à mesure que nous approchons du prochain point de contrôle, la fraction d'enregistrement delta commence à croître et à se réinitialiser au début du prochain point de contrôle. Les mesures de certains tests synthétiques ont montré que la part des instantanés de page dans le volume total des enregistrements physiques atteint 90 %.

L'idée de la compression d'instantanés de page WAL est de compresser des instantanés de page à l'aide d'un outil de compression de page prêt à l'emploi (voir compression de page disque). Dans le même temps, dans WAL, les enregistrements sont enregistrés séquentiellement en mode ajout uniquement et il n'est pas nécessaire de lier les enregistrements aux limites des blocs du système de fichiers, donc ici, contrairement au mécanisme de compression des pages disque, nous n'avons pas besoin de fichiers fragmentés à tout; par conséquent, ce mécanisme ne fonctionnera pas seulement sur le système d'exploitation Linux. De plus, peu importe à quel point nous avons pu compresser la page. Même si nous libérons 1 octet, c'est déjà un résultat positif et nous pouvons enregistrer les données compressées dans WAL, contrairement à la compression de page disque, où nous enregistrons la page compressée uniquement si nous avons libéré plus d'un bloc du système de fichiers.

Les pages sont des données hautement compressibles, leur part dans le volume total WAL est très élevée, donc sans changer le format du fichier WAL, nous pouvons obtenir une réduction significative de sa taille. La compression, y compris les enregistrements logiques, nécessiterait un changement de format et une perte de compatibilité, par exemple pour les consommateurs externes susceptibles d'être intéressés par les enregistrements logiques, mais n'entraînerait pas une réduction significative de la taille du fichier.

Comme pour la compression des pages disque, la compression des instantanés de pages WAL peut utiliser les algorithmes de compression ZSTD, LZ4, Snappy, ainsi que le mode SKIP_GARBAGE.

Impact sur les performances

Il n'est pas difficile de remarquer que l'activation directe de la compression des instantanés de pages WAL n'affecte que les threads qui écrivent des données dans la mémoire des pages, c'est-à-dire les threads qui modifient les données dans les caches. La lecture des enregistrements physiques depuis WAL n'a lieu qu'une seule fois, au moment où le nœud est remonté après une chute (et seulement s'il tombe lors d'un point de contrôle).

Cela affecte les threads qui modifient les données de la manière suivante : nous obtenons un effet négatif (CPU) en raison de la nécessité de compresser la page à chaque fois avant d'écrire sur le disque, et un effet positif (E/S disque) en raison d'une réduction de la quantité de données écrites. En conséquence, tout est simple ici : si les performances du système sont limitées par le CPU, nous obtenons une légère dégradation, si elles sont limitées par les E/S disque, nous obtenons une augmentation.

Indirectement, la réduction de la taille des WAL affecte également (positivement) les flux qui transfèrent les segments WAL dans les flux d'archive et de compactage des WAL.

Des tests de performances réels dans notre environnement utilisant des données synthétiques ont montré une légère augmentation (le débit a augmenté de 10 à 15 %, la latence a diminué de 10 à 15 %).

Comment activer et configurer

Version minimale d'Apache Ignite : 2.8. Activez et configurez comme suit :

  • Il doit y avoir un module ignite-compression dans le chemin de classe. Par défaut, il se trouve dans la distribution Apache Ignite dans le répertoire libs/optional et n'est pas inclus dans le chemin de classe. Vous pouvez simplement déplacer le répertoire d'un niveau vers libs, puis lorsque vous l'exécuterez via ignite.sh, il sera automatiquement activé.
  • La persistance doit être activée (Activée via DataRegionConfiguration.setPersistenceEnabled(true)).
  • Le mode de compression doit être défini à l'aide de la méthode DataStorageConfiguration.setWalPageCompression(), la compression est désactivée par défaut (mode DISABLED).
  • En option, vous pouvez définir le niveau de compression à l'aide de la méthode DataStorageConfiguration.setWalPageCompression(), voir la javadoc pour la méthode pour les valeurs valides pour chaque mode.

Conclusion

Les mécanismes de compression de données considérés dans Apache Ignite peuvent être utilisés indépendamment les uns des autres, mais toute combinaison d'entre eux est également acceptable. Comprendre leur fonctionnement vous permettra de déterminer dans quelle mesure ils sont adaptés à vos tâches dans votre environnement et ce que vous devrez sacrifier lors de leur utilisation. La compression des pages disque est conçue pour compresser le stockage principal et peut donner un taux de compression moyen. La compression des instantanés de pages WAL donnera un degré de compression moyen pour les fichiers WAL et améliorera probablement même les performances. Le compactage des WAL n'aura pas d'effet positif sur les performances, mais réduira autant que possible la taille des fichiers WAL en supprimant les enregistrements physiques.

Source: habr.com

Ajouter un commentaire