Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Je vous suggère de lire la transcription du rapport de début 2016 d'Andrey Salnikov « Erreurs typiques dans les applications qui conduisent à un gonflement dans PostgreSQL »

Dans ce rapport, j'analyserai les principales erreurs rencontrées dans les applications lors de la conception et de l'écriture du code. Je ne prendrai en compte que les erreurs qui entraînent une surcharge de PostgreSQL. En règle générale, cela marque le début de la fin des performances de votre système, même si, initialement, aucune condition préalable n'était requise.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Je suis heureux de vous accueillir tous ! Ce rapport n'est pas aussi technique que le précédent de mon collègue. Il s'adresse principalement aux développeurs de systèmes back-end, car nous avons un nombre important de clients. Et ils commettent tous les mêmes erreurs. Je vais vous les présenter. J'expliquerai les conséquences fatales et néfastes de ces erreurs.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Pourquoi des erreurs se produisent-elles ? Elles surviennent pour deux raisons : par caprice, peut-être que cela fonctionnera, et par ignorance de certains mécanismes qui interviennent entre la base de données et l'application, ainsi que dans la base de données elle-même.

Je vais vous donner trois exemples, illustrés par des images horribles, illustrant la gravité de la situation. Je vous expliquerai brièvement le mécanisme en jeu, comment y faire face et quelles méthodes préventives utiliser pour éviter les erreurs. Je vous présenterai des outils auxiliaires et vous fournirai des liens utiles.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

J'ai utilisé une base de données de test contenant deux tables : une contenant les comptes clients et l'autre les transactions sur ces comptes. Nous mettons régulièrement à jour les soldes de ces comptes.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Données initiales de la table : elles sont assez petites (2 Mo). Le temps de réponse de la base de données, et plus particulièrement de la table, est également très bon. La charge de la table est également assez élevée : 2 000 opérations par seconde.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Ce rapport vous présentera des graphiques pour vous aider à comprendre ce qui se passe. Il y aura toujours deux diapositives avec des graphiques. La première présente l'activité générale du serveur.

Dans ce cas, nous constatons que nous avons une petite table. L'index est petit, 2 Mo. Voici le premier graphique à gauche.

Le temps de réponse moyen du serveur est également stable et court. Voici le graphique en haut à droite.

Le graphique en bas à gauche représente les transactions les plus longues. On constate que les transactions sont exécutées rapidement. L'autovacuum ne fonctionne pas encore ici, car il s'agissait d'un test de démarrage. Il fonctionnera ultérieurement et nous sera utile.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

La deuxième diapositive sera toujours consacrée à la table testée. Dans ce cas, nous mettons constamment à jour les soldes des comptes clients. Nous constatons que le temps de réponse moyen pour l'opération de mise à jour est plutôt bon, inférieur à une milliseconde. Nous constatons également que les ressources processeur (graphique en haut à droite) sont consommées uniformément et sont relativement faibles.

Le graphique en bas à droite montre la quantité de mémoire d'exploitation et de mémoire disque utilisée pour rechercher la ligne souhaitée avant de la mettre à jour. Le nombre d'opérations, selon le tableau, est de 2 000 par seconde, comme indiqué au début.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Et voilà qu'une tragédie se produit. Pour une raison inconnue, une transaction oubliée depuis longtemps se produit. Les raisons sont généralement anodines :

  • L'un des problèmes les plus courants est que nous avons commencé à accéder à un service externe dans le code de l'application. Or, ce service ne répond pas. Autrement dit, nous avons ouvert une transaction, modifié la base de données et quitté l'application pour lire du courrier ou accéder à un autre service de notre infrastructure, mais, pour une raison inconnue, il ne répond pas. Notre session est alors bloquée ; on ignore quand elle sera résolue.
  • La deuxième situation se présente lorsque, pour une raison quelconque, une exception se produit dans notre code. Nous n'avons pas traité la clôture de la transaction dans l'exception. Une session est alors suspendue avec une transaction ouverte.
  • Le dernier cas est également assez courant : il s'agit d'un code de mauvaise qualité. Certains frameworks ouvrent une transaction, mais celle-ci se bloque, sans que vous le sachiez dans l'application.

Où mènent de telles choses ?

Nos tables et index commencent à gonfler considérablement. C'est exactement le même effet de gonflement. Pour la base de données, cela se traduira par une augmentation considérable du temps de réponse et de la charge sur le serveur. Par conséquent, notre application en souffrira. En effet, si dans le code vous avez consacré 10 millisecondes à une requête vers la base de données, 10 millisecondes à votre logique, votre fonction a fonctionné pendant 20 millisecondes. Et votre situation sera alors désastreuse.

Voyons ce qui se passe. Le graphique en bas à gauche montre une transaction très longue. En haut à gauche, on constate que la taille de la table est passée de deux à 300 mégaoctets. Parallèlement, la quantité de données dans la table n'a pas changé, ce qui signifie qu'elle contient beaucoup de données inutiles.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

La situation générale concernant le temps de réponse moyen du serveur a également évolué de plusieurs ordres de grandeur. Autrement dit, toutes les requêtes adressées au serveur ont commencé à chuter significativement. Parallèlement, des processus internes Postgres, représentés par l'autovacuum, ont démarré, essayant d'effectuer une action et consommant des ressources.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Que se passe-t-il avec notre table ? Même chose. Le temps de réponse moyen de la table a augmenté de plusieurs ordres de grandeur. Si l'on examine plus précisément les ressources consommées, on constate une forte augmentation de la charge processeur. Voici le graphique en haut à droite. Cette augmentation est due au fait que le processeur doit trier un grand nombre de lignes inutiles à la recherche de celle qui lui est nécessaire. Voici le graphique en bas à droite. Par conséquent, le nombre d'appels par seconde a fortement diminué, car la base de données ne peut plus traiter le même nombre de requêtes.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Il faut revenir à la vie. On se connecte et on découvre que les transactions longues posent problème. On identifie et on interrompt cette transaction. Et tout redevient normal. Tout fonctionne comme prévu.

Nous nous sommes calmés, mais au bout d'un moment, nous avons constaté que l'application ne fonctionnait plus comme avant l'urgence. Les requêtes étaient toujours traitées plus lentement, nettement plus lentement. Une fois et demie à deux fois plus lentement dans mon exemple. La charge du serveur était également plus élevée qu'avant l'urgence.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

La question est : « Qu’arrive-t-il à la base de données en ce moment ? » La situation suivante se produit. Sur le graphique des transactions, on constate qu’elle s’est arrêtée et qu’il n’y a plus de transactions longues. Cependant, la taille des tables a considérablement augmenté pendant le crash. Et elle n’a pas diminué depuis. Le temps moyen de réponse de la base de données s’est stabilisé. Les réponses semblent arriver correctement, à une vitesse acceptable pour nous. Autovacuum est devenu plus actif et a commencé à traiter la table, car il doit traiter davantage de données.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Concernant la table testée avec les comptes dont nous modifions les soldes, le temps de réponse à une requête semble être revenu à la normale. En réalité, il est une fois et demie plus long.

La charge du processeur montre qu'elle n'est pas revenue à la valeur requise avant le crash. Les raisons sont cachées dans le graphique en bas à droite. Il est clair qu'une partie de la mémoire est en cours de tri. Autrement dit, pour trouver la ligne requise, nous gaspillons les ressources du serveur de base de données en triant les données inutiles. Le nombre de transactions par seconde s'est stabilisé.

Globalement, c'est bien, mais la situation est pire qu'avant. Dégradation évidente de la base de données due à notre application qui fonctionne avec cette base de données.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Et pour comprendre ce qui se passe, si vous n'avez pas lu le rapport précédent, un peu de théorie. Théorie du processus interne. Pourquoi l'autovacuum et à quoi sert-il ?

En bref, pour mieux comprendre. À un moment donné, nous avons une table. Dans cette table, nous trouvons des lignes. Ces lignes peuvent être actives, en cours de traitement, ou nécessaires. Sur l'image, elles sont marquées en vert. Il existe également des lignes mortes qui ont déjà fonctionné, ont été mises à jour et de nouveaux enregistrements sont apparus. Elles sont marquées comme n'étant plus intéressantes pour la base de données. Cependant, leur présence dans la table est due aux particularités de Postgres.

Pourquoi avons-nous besoin d'autovacuum ? À un moment donné, autovacuum contacte la base de données et lui demande : « Veuillez me fournir l'identifiant de la transaction la plus ancienne actuellement ouverte dans la base de données. » La base de données renvoie cet identifiant. Autovacuum, en se basant sur celui-ci, parcourt les lignes de la table. S'il constate que certaines lignes ont été modifiées par des transactions beaucoup plus anciennes, il est alors autorisé à les marquer comme des lignes réutilisables ultérieurement en y écrivant de nouvelles données. Il s'agit d'un processus d'arrière-plan.

À ce stade, nous continuons à travailler avec la base de données et à apporter des modifications à la table. Nous écrivons de nouvelles données sur ces lignes, que nous pouvons réutiliser. Ainsi, nous obtenons un cycle : à chaque fois, d'anciennes lignes obsolètes apparaissent, remplacées par de nouvelles lignes nécessaires. C'est un état normal pour le fonctionnement de PostgreSQL.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Que s'est-il passé lors de l'accident ? Comment ce processus s'est-il déroulé ?

Nous avions une table dans un état différent, avec des lignes actives et des lignes mortes. Autovacuum est arrivé. Il a demandé à la base de données quelle était notre transaction la plus ancienne et quel était son identifiant. Il a reçu cet identifiant, qui pouvait dater de plusieurs heures ou de dix minutes. Cela dépend de la charge de votre base de données. Il a ensuite cherché des lignes à marquer comme réutilisées. Il n'a pas trouvé de telles lignes dans notre table.

Mais à ce stade, nous continuons à travailler avec la table. Nous y effectuons des opérations, mettons à jour, modifions des données. Et que doit faire la base de données à ce moment-là ? Elle n'a d'autre choix que d'ajouter de nouvelles lignes à la fin de la table existante. Et ainsi, la taille de notre table commence à gonfler.

En réalité, nous avons besoin de lignes vertes pour fonctionner. Or, face à un tel problème, nous constatons que le pourcentage de lignes vertes est extrêmement faible dans le volume total de la table.

Et lorsque nous exécutons une requête, la base de données doit parcourir toutes les lignes, rouges et vertes, pour trouver la ligne nécessaire. L'encombrement de la table avec des données inutiles est appelé « gonflement », ce qui consomme également de l'espace disque. Vous vous souvenez, c'était 2 Mo, puis 300 Mo ? Transformez maintenant les mégaoctets en gigaoctets et vous perdrez rapidement toutes vos ressources disque.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Quelles conséquences cela pourrait-il avoir pour nous ?

  • Dans mon exemple, la table et l'index ont été multipliés par 150. Certains de nos clients ont connu des cas plus graves, lorsqu'ils ont tout simplement manqué d'espace disque.
  • La taille des tables elles-mêmes ne diminuera jamais. Autovacuum peut parfois couper la fin de la table s'il n'y a que des lignes mortes. Mais comme la rotation est constante, une ligne verte peut rester bloquée à la fin et ne pas être mise à jour, tandis que toutes les autres, situées au début de la table, seront écrites. Cependant, ce risque est tellement improbable que la taille de votre table diminuera qu'il ne faut pas l'espérer.
  • La base de données doit trier toute cette pile de lignes inutiles. Nous gaspillons ainsi des ressources disque, processeur et électricité.
  • Et cela affecte directement notre application, car si au début nous passions 10 millisecondes sur une requête et 10 millisecondes sur notre code, lors du crash, nous avons commencé à passer une seconde sur une requête et 10 millisecondes sur le code, ce qui a entraîné une baisse considérable des performances de l'application. Une fois le crash résolu, nous avons commencé à passer 20 millisecondes sur une requête et 10 millisecondes sur le code. Cela signifie que nos performances ont tout de même été divisées par XNUMX. Tout cela à cause d'une transaction bloquée, et peut-être de notre faute.
  • Et la question est : « Comment pouvons-nous tout récupérer ? » pour que tout aille bien pour nous et que les demandes commencent à arriver aussi vite qu’avant l’accident.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Pour cela, il y a un certain cycle de travail qui est réalisé.

Tout d'abord, nous devons identifier les tables problématiques qui ont grossi. Nous comprenons que certaines tables sont en cours d'écriture plus activement, tandis que d'autres le sont moins. Pour cela, l'extension est utilisée. pgstattupleEn installant cette extension, vous pouvez écrire des requêtes qui vous aideront à trouver des tables qui sont devenues suffisamment gonflées.

Une fois ces tables trouvées, il faut les compresser. Il existe déjà des outils pour cela. Dans notre entreprise, nous en utilisons trois. Le premier est VACUUM FULL, un outil intégré. Il est cruel, impitoyable et parfois très utile. Pg_repack и pgcompacttable - Ce sont des utilitaires tiers pour compresser les tables. Ils sont plus respectueux de la base de données.

Ils s'utilisent selon vos préférences. Je vous en parlerai à la fin. L'essentiel est qu'il existe trois outils. Vous avez le choix.

Après avoir tout réparé et vérifié que tout va bien, nous devons savoir comment éviter cette situation à l’avenir :

  • Cela peut être évité assez facilement : il suffit de surveiller la durée des sessions sur le serveur maître. Sessions particulièrement dangereuses en état de transaction inactifCe sont ceux qui viennent d'ouvrir une transaction, ont fait quelque chose et sont partis, ou ont simplement raccroché, perdus dans le code.
  • Il est important pour vous, développeurs, de tester votre code lorsque ces situations se présentent. Ce n'est pas difficile à réaliser. Ce sera une vérification utile. Vous éviterez ainsi bien des problèmes « enfantins » liés aux transactions longues.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Dans ces graphiques, je voulais vous montrer comment le comportement de la table et de la base de données a changé après l'exécution de VACUUM FULL sur la table dans ce cas. Il ne s'agit pas d'une production.

La taille de la table est immédiatement revenue à son état de fonctionnement normal, soit quelques mégaoctets. Cela n'a pas eu d'impact significatif sur le temps de réponse moyen du serveur.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Plus précisément, pour notre table de test, où nous avons mis à jour les soldes des comptes, nous constatons que le temps de réponse moyen pour une requête de mise à jour des données de la table est revenu à son niveau d'avant le crash. Les ressources consommées par le processeur pour exécuter cette requête sont également revenues à leur niveau d'avant le crash. Le graphique en bas à droite montre que nous trouvons désormais immédiatement la ligne exacte dont nous avons besoin, sans passer par les nombreuses lignes mortes qui existaient avant la compression de la table. Le temps moyen de requête est resté à peu près le même. Mais ici, il s'agit probablement d'une erreur matérielle.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

C'est là que s'arrête la première histoire. C'est la plus courante. Et cela arrive à tout le monde, quelle que soit l'expérience du client ou les compétences des programmeurs. Tôt ou tard, cela arrive.

La deuxième histoire, dans laquelle nous répartissons la charge et optimisons les ressources du serveur

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

  • Nous avons déjà grandi et sommes devenus des personnes sérieuses. Nous comprenons que nous avons une réplique et qu'il serait judicieux d'équilibrer la charge : écrire sur le maître et lire depuis la réplique. Ce problème se pose généralement lorsque nous voulons préparer des rapports ou effectuer des opérations ETL. L'entreprise en est ravie. Elle souhaite disposer de rapports variés et riches en analyses complexes.
  • Les rapports prennent des heures, car des analyses complexes ne peuvent être calculées en quelques millisecondes. Nous, courageux, écrivons le code. Nous l'intégrons à l'application enregistrée sur le maître et exécutons les rapports sur la réplique.
  • Nous répartissons la charge.
  • Tout fonctionne à merveille. Nous sommes formidables.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Et à quoi ressemble cette situation ? Sur ces graphiques, j'ai également ajouté la durée des transactions de la réplique pour la durée des transactions. Tous les autres graphiques se réfèrent uniquement au serveur maître.

Le tableau des rapports s'est agrandi. Ils sont plus nombreux. Nous constatons que le temps de réponse moyen du serveur est stable. Nous constatons qu'une transaction longue est en cours d'exécution sur la réplique depuis deux heures. Nous constatons le fonctionnement silencieux de l'autovacuum, qui traite les lignes mortes. Et tout va bien.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Pour la table testée en particulier, nous continuons à mettre à jour les soldes des comptes. Nous affichons également un temps de réponse stable aux demandes et une consommation de ressources stable. Tout va bien pour nous.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Tout va bien jusqu'à ce que ces rapports se déclenchent à cause d'un conflit avec la réplication. Et ils se déclenchent à une fréquence constante.

Nous allons sur Internet et commençons à comprendre pourquoi cela se produit. Et nous trouvons une solution.

La première solution consiste à augmenter le délai de réplication. Nous savons que notre rapport s'exécute pendant 3 heures. Nous avons défini le délai de réplication à 3 heures. Nous avons tout lancé, mais nous rencontrons toujours des problèmes de déclenchement de rapports.

Nous voulons que tout soit parfait. Nous allons plus loin. Et nous trouvons un paramètre intéressant sur Internet : hot_standby_feedback. Nous l'activons. Hot_standby_feedback nous permet de maintenir l'autovacuum sur le maître. Ainsi, nous éliminons complètement les conflits de réplication. Et tout fonctionne parfaitement avec les rapports.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Et que se passe-t-il actuellement avec le serveur maître ? La situation est désastreuse. Nous observons maintenant les graphiques après l'activation de ces deux paramètres. Nous constatons que la session sur la réplique a commencé à influencer la situation sur le serveur maître. Elle a effectivement un impact, car elle a suspendu l'autovacuum, qui nettoie les lignes mortes. La taille de notre table a encore explosé. Le temps moyen d'exécution des requêtes pour l'ensemble de la base de données a également explosé. Les autovacuums ont légèrement souffert.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Concernant notre table, nous constatons une forte augmentation de la mise à jour des données. La consommation de ressources processeur a également fortement augmenté. Nous trions à nouveau un grand nombre de lignes inutiles. Le temps de réponse de cette table et le nombre de transactions ont diminué.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

À quoi cela ressemblera-t-il si nous ne savons pas de quoi j’ai parlé auparavant ?

  • Nous commençons à chercher les problèmes. Si nous avons rencontré des problèmes lors de la première partie, nous savons que cela pourrait être dû à une transaction trop longue et nous nous tournons vers le Master. Le problème vient du Master. Il tremble. Il chauffe, sa charge moyenne est inférieure à cent.
  • Les requêtes y sont lentes, mais nous n'y voyons aucune transaction longue. Et nous ne comprenons pas ce qui se passe. Nous ne savons pas où chercher.
  • Nous vérifions le matériel du serveur. Notre raid a peut-être échoué. Notre barre de mémoire a peut-être grillé. Tout peut arriver. Mais non, les serveurs sont neufs, tout fonctionne parfaitement.
  • Tout le monde court dans tous les sens : administrateurs, développeurs et directeur. Rien n'y fait.
  • Et à un moment donné, tout commence soudainement à se corriger.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

À ce stade, la demande sur la réplique a été traitée et envoyée. Nous avons reçu un rapport. L'activité est toujours satisfaisante. Comme vous pouvez le constater, notre table a de nouveau progressé et ne devrait pas diminuer. Sur le graphique de la session, j'ai conservé un extrait de cette longue transaction de la réplique afin que vous puissiez évaluer le temps nécessaire à la stabilisation de la situation.

La session est terminée. Et ce n'est qu'après un certain temps que le serveur est plus ou moins en ordre. Le temps de réponse moyen aux requêtes sur le serveur maître revient à la normale. Car, enfin, Autovacuum a eu l'occasion de nettoyer et de marquer ces lignes mortes. Et il a commencé son travail. Et aussi vite qu'il le fera, aussi vite nous serons en ordre.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Dans la table de test, où nous mettons à jour les soldes des comptes, nous observons exactement la même situation. Le temps moyen de mise à jour des comptes se normalise progressivement. Les ressources consommées par le processeur diminuent également. Et le nombre de transactions par seconde revient à la normale. Mais là encore, il ne revient pas à la normale d'avant le crash.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Dans tous les cas, on obtient une baisse de performance, comme dans le premier cas, d'une fois et demie à deux fois, et parfois même plus.

Il semble que tout ait été fait correctement. Nous avons bien réparti la charge. Le matériel n'est pas inactif. Nous avons réparti les demandes judicieusement, mais tout s'est mal passé.

  • Vous n'activez pas hot_standby_feedback ? Oui, il est déconseillé de l'activer sans raison valable. En effet, ce paramètre affecte directement le serveur maître et y suspend l'autovacuum. En l'activant sur une réplique et en l'oubliant, vous risquez de tuer le maître et de provoquer de graves problèmes avec l'application.
  • Augmenter max_standby_streaming_delay ? Oui, pour les rapports. Si vous avez un rapport de trois heures et que vous ne souhaitez pas qu'il plante à cause de conflits de réplication, augmentez simplement le délai. Un rapport long n'a jamais besoin de données déjà arrivées dans la base de données. Si vous avez un rapport de trois heures, cela signifie que vous l'exécutez sur une période de données ancienne. Et pour vous, un délai de trois ou six heures ne changera rien, mais vous recevrez des rapports stables et ne rencontrerez aucun problème de plantage.
  • Naturellement, vous devez contrôler les longues sessions sur les réplicas, surtout si vous avez décidé d'activer la fonction hot_standby_feedback sur le réplica. Car tout peut arriver. Vous avez donné ce réplica au développeur pour qu'il teste les requêtes. Il a écrit une requête un peu folle, l'a lancée et est allé boire un thé, et nous avons obtenu un maître fonctionnel. Ou nous avons laissé la mauvaise application y entrer. Les situations sont variées. Les sessions sur les réplicas doivent être contrôlées avec autant de soin que sur le maître.
  • Si vous avez des requêtes rapides et longues sur des réplicas, il est préférable de les fractionner pour répartir la charge. Ceci est un lien vers streaming_delay. Pour les requêtes rapides, utilisez un réplica avec un léger délai de réplication. Pour les requêtes de reporting longues, utilisez un réplica avec un décalage de 6 heures, voire d'un jour. C'est une situation tout à fait normale.

Nous éliminons les conséquences de la même manière :

  • On retrouve des tables gonflées.
  • Et nous le compressons avec l'outil le plus pratique qui nous convient.

La deuxième histoire se termine ici. Passons à la troisième.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

C'est également assez courant chez nous, dans le cadre duquel nous effectuons des migrations.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

  • Tout produit logiciel évolue. Ses exigences évoluent. Nous souhaitons évoluer dans tous les cas. Il arrive que nous devions mettre à jour les données du tableau, notamment effectuer une mise à jour pour la migration vers les nouvelles fonctionnalités implémentées dans le cadre de notre développement.
  • L'ancien format de données n'est pas satisfaisant. Passons maintenant à la deuxième table, où se trouvent les transactions sur ces comptes. Supposons qu'elles soient en roubles, mais que nous ayons décidé d'améliorer la précision et de les convertir en kopecks. Pour cela, nous devons effectuer une mise à jour : multiplier le champ contenant le montant de la transaction par cent.
  • Aujourd'hui, nous utilisons des moyens automatisés de contrôle des versions de bases de données. Par exemple, LiquibaseNous y enregistrons notre migration. Nous la testons sur notre base de test. Tout se passe bien. La mise à jour est effectuée. Elle bloque le travail pendant un certain temps, mais nous obtenons des données mises à jour. Nous pouvons alors lancer de nouvelles fonctionnalités. Nous avons tout testé et vérifié. Tout a été confirmé.
  • Nous avons réalisé les travaux planifiés et effectué la migration.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Voici la migration avec la mise à jour qui vous est présentée. Comme il s'agit de mes transactions de compte, la table faisait 15 Go. Et comme nous mettons à jour chaque ligne, nous avons gonflé la table deux fois avec la mise à jour, car nous avons réécrit chaque ligne.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Pendant la migration, nous n'avons rien pu faire avec cette table, car toutes les requêtes étaient en file d'attente et attendaient la fin de la mise à jour. J'attire votre attention sur les chiffres de l'axe vertical. Autrement dit, le temps de requête moyen avant la migration est d'environ 5 millisecondes et la charge processeur est inférieure à 7,5.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Nous avons effectué la migration et avons à nouveau rencontré des problèmes.

La migration a réussi, mais :

  • L’exécution des anciennes fonctionnalités prend désormais plus de temps.
  • La table a encore grandi en taille.
  • La charge du serveur est à nouveau devenue plus importante qu'elle ne l'était.
  • Et, naturellement, nous continuons à peaufiner la fonctionnalité qui fonctionnait bien, nous l'avons un peu améliorée.

Et c'est encore du ballonnement, qui ruine encore nos vies.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Je démontre ici que la table, comme dans les deux cas précédents, ne reviendra pas à sa taille initiale. La charge moyenne du serveur semble adéquate.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Si l'on examine le tableau des comptes, on constate que le temps moyen de requête a doublé par rapport à ce tableau. La charge processeur et le nombre de lignes mémoire triées ont dépassé 7,5, voire diminué. Ce nombre a doublé pour les processeurs et multiplié par 2 pour les opérations par blocs, ce qui a entraîné une dégradation des performances du serveur et, par conséquent, des performances de notre application. Parallèlement, le nombre d'appels est resté quasiment inchangé.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

L'essentiel ici est de comprendre comment effectuer correctement ces migrations. Et elles doivent être effectuées. Nous effectuons ces migrations assez régulièrement.

  • Ces migrations importantes ne sont pas automatiques. Elles doivent toujours être contrôlées.
  • Il faut que ce soit supervisé par une personne compétente. Si vous avez un administrateur de bases de données dans votre équipe, confiez-le-lui. C'est son travail. Sinon, confiez-le à la personne la plus expérimentée, celle qui sait travailler avec les bases de données.
  • Nous préparons toujours un nouveau schéma de base de données par étapes, même si nous mettons à jour une colonne, c'est-à-dire à l'avance avant le déploiement de la nouvelle version de l'application :
  • De nouveaux champs sont ajoutés dans lesquels nous écrirons les données mises à jour.
  • Nous transférons les données de l'ancien champ vers le nouveau par petites portions. Pourquoi procédons-nous ainsi ? Tout d'abord, nous contrôlons constamment le processus. Nous savons que nous avons déjà transféré un certain nombre de lots et qu'il en reste encore beaucoup.
  • Et le deuxième effet positif est qu'entre chaque lot, nous fermons la transaction, en ouvrons une nouvelle, et cela donne à l'autovacuum la possibilité de travailler selon le tableau, de marquer les dates limites de réutilisation.
  • Pour les lignes qui apparaîtront pendant le fonctionnement de l'application (l'ancienne application étant toujours en cours d'exécution), nous ajoutons un déclencheur qui écrit les nouvelles valeurs dans les nouveaux champs. Dans notre cas, il s'agit de multiplier l'ancienne valeur par cent.
  • Si nous sommes vraiment têtus et souhaitons conserver le même champ, une fois toutes les migrations terminées et avant de déployer une nouvelle version de l'application, nous renommons simplement les champs : les anciens champs avec un nom inventé, et les nouveaux champs avec les anciens.
  • Et seulement après cela, nous lançons la nouvelle version de l'application.

Et en même temps, nous ne souffrirons pas de ballonnements et ne subirons pas de baisse de performances.

C'est ici que se termine la troisième histoire.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat_approx.sql

Et maintenant un peu plus de détails sur les outils que j'ai mentionnés dans la toute première histoire.

Avant de rechercher bloat, vous devez installer l'extension pgstattuple.

Pour vous éviter d'avoir à formuler des requêtes, nous les avons déjà intégrées à notre travail. Vous pouvez les utiliser. Deux requêtes sont présentées ici.

  • Le premier fonctionne assez longtemps, mais il vous montrera les valeurs de gonflement exactes selon le tableau.
  • La seconde méthode est plus rapide et très efficace pour évaluer rapidement la présence de surcharge dans une table. Il est également important de comprendre qu'une table Postgres est toujours en surcharge. C'est une caractéristique de son modèle MVCC.
  • Un gonflement de 20 % est normal pour les tables dans la plupart des cas. Vous n'avez donc pas à vous soucier de la compression de cette table.

Nous avons compris comment identifier les tables qui sont devenues gonflées et quand elles sont devenues gonflées de données inutiles.

Maintenant, comment remédier aux ballonnements :

  • Si nous disposons d'une petite table et de bons disques, c'est-à-dire d'une taille maximale d'un gigaoctet, il est tout à fait possible d'utiliser VACUUM FULL. Ce verrou exclusif sur la table est suffisant pendant quelques secondes, mais il s'exécute rapidement et efficacement. Que fait VACUUM FULL ? Il utilise un verrou exclusif sur la table et réécrit les lignes actives des anciennes tables vers la nouvelle. Enfin, il les permute. Il supprime les anciens fichiers et les remplace par de nouveaux. Cependant, pendant toute la durée de son fonctionnement, il utilise un verrou exclusif sur la table. Cela signifie que vous ne pourrez rien faire avec cette table : ni y écrire, ni y lire, ni la modifier. De plus, VACUUM FULL nécessite de l'espace disque supplémentaire pour écrire des données.
  • Outil suivant pg_repackSon principe est très similaire à VACUUM FULL, car il réécrit également les données des anciens fichiers vers les nouveaux et les remplace dans la table. Cependant, il ne verrouille pas la table de manière exclusive dès le début de son exécution, mais uniquement lorsqu'il dispose déjà de données prêtes à remplacer les fichiers. Ses besoins en ressources disque sont similaires à ceux de VACUUM FULL. Un espace disque supplémentaire est nécessaire, ce qui peut parfois s'avérer critique pour les tables de plusieurs téraoctets. De plus, il est assez gourmand en ressources processeur, car il gère activement les entrées-sorties.
  • Le troisième utilitaire est pgcompacttableIl est plus économe en ressources, car son fonctionnement est légèrement différent. L'intérêt principal de pgcompacttable est de déplacer toutes les lignes actives au début de la table lors des mises à jour. Il crée ensuite un vide sur cette table, car nous savons qu'il y a des lignes actives au début et des lignes mortes à la fin. Le vide lui-même coupe cette queue, ce qui signifie qu'il ne nécessite pas beaucoup d'espace disque supplémentaire. De plus, il peut être compressé en termes de ressources.

C'est tout une question d'outils.

Erreurs d'application typiques qui entraînent des ballonnements dans postgresql. Andreï Salnikov

Si vous trouvez le sujet du gonflement intéressant et souhaitez approfondir le sujet, voici quelques liens utiles :

  • https://www.slideshare.net/alexius2Mb/where-is-the-space-postgres - Ceci est un rapport de mon collègue. Il décrit de manière générale l'utilisation de l'espace de Postgres au cours de son fonctionnement et de sa durée de vie. Il contient également un article technique très complet et détaillé destiné aux administrateurs de bases de données concernant le gonflement.
  • https://github.com/dataegret/pg-utils – Ceci est un lien vers notre dépôt où nous conservons un ensemble de scripts utiles pour vérifier l'état de la base de données. Vous y trouverez des scripts pour rechercher des données saturées.
  • Третья и quatrième Liens vers des outils qui vous aideront à compresser vos tables.
  • http://blog.dataegret.com/2Mb018/03/postgresql-bloatbusters.html - Ceci est un article de mon collègue. Il y analyse le gonflement de manière très sérieuse et détaillée, à un niveau proche de celui des administrateurs.

J'ai essayé de raconter une histoire d'horreur aux développeurs, car ils sont nos clients directs des bases de données et devraient comprendre à quoi elles aboutissent et quelles actions mènent. J'espère y être parvenu. Merci de votre attention !

des questions

Merci pour le rapport ! Vous avez expliqué comment identifier les problèmes. Mais comment les éviter ? J'ai eu une situation où des requêtes étaient bloquées, non seulement parce qu'elles accédaient à des services externes, mais aussi parce qu'il s'agissait de simples jointures intempestives. Il y avait aussi de minuscules requêtes inoffensives qui sont restées bloquées pendant une journée, puis ont commencé à faire des bêtises. C'est très similaire à ce que vous décrivez. Comment suivre cela ? Regarder constamment quelle requête est bloquée ? Comment éviter cela ?

Dans ce cas, il s'agit d'une tâche qui incombe aux administrateurs de votre entreprise, pas nécessairement au DBA.

Je suis administrateur.

PostgreSQL dispose d'une vue appelée pg_stat_activity qui affiche les requêtes bloquées. Vous pouvez également voir depuis combien de temps elles sont bloquées.

Dois-je venir regarder toutes les 5 minutes ?

Configurez Cron et vérifiez. Si vous avez une demande à long terme, écrivez une lettre et c'est tout. Autrement dit, vous n'avez pas besoin de regarder avec les yeux, c'est automatisé. Vous recevrez une lettre, vous y réagirez. Ou vous pourrez déclencher automatiquement.

Y a-t-il des raisons évidentes pour lesquelles cela se produit ?

J'en ai énuméré quelques-uns. D'autres sont des exemples plus complexes. Et là, la discussion pourrait durer longtemps.

Merci pour le rapport ! Je voulais apporter une précision à propos de l'utilitaire pg_repack. S'il ne crée pas de verrou exclusif, alors…

Elle fabrique un cadenas exclusif.

... Je risque alors de perdre des données. Mon application ne devrait rien écrire pendant ce temps ?

Non, cela fonctionne discrètement avec la table : pg_repack transfère d'abord toutes les lignes actives présentes. Naturellement, une entrée dans la table se produit à ce niveau. Il ajoute simplement cette fin.

Alors il le fait à la fin ?

À la fin, il prend un verrou exclusif sur l'échange de ces fichiers.

Sera-t-il plus rapide que VACUUM FULL ?

Dès son démarrage, VACUUM FULL a immédiatement pris un verrou exclusif. Et tant qu'il n'aura pas terminé, il ne le libérera pas. De plus, pg_repack prend un verrou exclusif uniquement au moment du remplacement des fichiers. À ce moment-là, vous n'y écrirez plus, mais les données ne seront pas perdues, tout fonctionnera correctement.

Bonjour ! Vous parliez de l'autovacuum. Il y avait un graphique avec des cellules d'enregistrement rouges, jaunes et vertes. Autrement dit, les jaunes étaient marquées comme supprimées. Et donc, on peut y écrire quelque chose de nouveau ?

Oui. Postgres ne supprime pas les lignes. Il possède des spécificités. Si nous mettons à jour une ligne, nous marquons l'ancienne comme supprimée. L'identifiant de transaction qui a modifié cette ligne apparaît et nous écrivons une nouvelle ligne. Des sessions peuvent potentiellement les lire. À un moment donné, elles deviennent très anciennes. L'essence même d'autovacuum est de parcourir ces lignes et de les marquer comme inutiles. Vous pouvez alors y réécrire des données.

Je comprends. Mais la question est un peu hors sujet. Je n'ai pas terminé. Imaginons que nous ayons un tableau. Il contient des champs de taille variable. Et si j'essaie d'insérer quelque chose de nouveau, il risque de ne pas tenir dans l'ancienne cellule.

Non, la ligne entière est mise à jour de toute façon. Postgres propose deux modèles de stockage de données. Il choisit en fonction du type de données. Il y a les données stockées directement dans la table, et il y a aussi les données TOS. Il s'agit de grandes quantités de données : texte, JSON. Elles sont stockées dans des tables distinctes. Le même phénomène de gonflement se produit pour ces tables : tout est identique, elles sont simplement extraites séparément.

Merci pour le rapport ! Est-il acceptable d'utiliser le délai d'expiration des instructions pour limiter la durée des requêtes ?

Très acceptable. Nous l'utilisons partout. Et comme nous ne disposons pas de nos propres services, nous proposons une assistance à distance et avons des clients très variés. Et tout le monde en est satisfait. Nous avons des tâches cron qui effectuent des vérifications. Nous convenons simplement avec le client de la durée des sessions, avant laquelle nous ne les interrompons pas. Cela peut être d'une minute, voire de dix minutes. Cela dépend de la charge de la base de données et de son objectif. Mais nous utilisons tous pg_stat_activity.

Merci pour le rapport ! J'essaie d'appliquer votre rapport à mes applications. Il semble que nous démarrions une transaction partout et la terminions explicitement partout. En cas d'exception, le retour arrière se produit quand même. Et puis j'y ai réfléchi. Après tout, une transaction peut démarrer implicitement. C'est probablement un indice pour la fille. Si je mets simplement à jour un enregistrement, la transaction démarrera-t-elle dans PostgreSQL et se terminera-t-elle uniquement lorsque la connexion sera déconnectée ?

Au niveau de l'application, cela dépend du pilote et de l'ORM utilisés. De nombreux paramètres sont disponibles. Si la validation automatique est activée, une transaction démarre et est immédiatement clôturée.

Donc il se ferme immédiatement après la mise à jour ?

Cela dépend des paramètres. J'en ai mentionné un : la validation automatique est activée. C'est assez courant. S'il est activé, la transaction est ouverte et fermée. Si vous n'avez pas explicitement indiqué « démarrer la transaction » et « terminer la transaction », mais simplement lancé une requête dans la session,

Bonjour ! Merci pour le rapport ! Imaginons que notre base de données ne cesse de croître et que le serveur manque d'espace. Existe-t-il des outils pour résoudre ce problème ?

Ce serait une bonne idée de surveiller l’espace serveur.

Par exemple, DBA est allé boire du thé, était dans un complexe hôtelier, etc.

Lorsqu'un système de fichiers est créé, au moins un espace de réserve est créé là où les données ne sont pas écrites.

Et si c'était complètement parti ?

On parle alors d'espace réservé, c'est-à-dire qu'il peut être libéré et, selon sa taille, vous obtenez de l'espace libre. Par défaut, je ne connais pas la quantité disponible. Dans un autre cas, vous pouvez fournir des disques afin de disposer de l'espace nécessaire à une opération de récupération. Vous pouvez supprimer certaines tables dont vous n'aurez certainement pas besoin.

Pas d'autres outils ?

Il s'agit toujours d'un travail manuel. La meilleure solution est déterminée sur place, car il existe des données critiques et d'autres non critiques. Pour chaque base de données et l'application qui l'utilise, cela dépend de l'entreprise. La décision est toujours prise sur place.

Merci pour le rapport ! J'ai deux questions. Premièrement, vous avez montré des diapositives montrant qu'en cas de blocage de transactions, l'espace de table et la taille de l'index augmentent. Ensuite, le rapport présentait plusieurs utilitaires permettant de compresser la table. Et qu'en est-il de l'index ?

Ils les emballent aussi.

Mais le vide n'affecte pas l'indice ?

Certains fonctionnent avec des index. Par exemple, pg_rapack et pgcompacttable. Vacuum recrée les index et les affecte. L'objectif de VACUUM FULL est de tout réécrire, c'est-à-dire qu'il fonctionne avec tout le monde.

Et la deuxième question : je ne comprenais pas pourquoi les rapports sur les réplications dépendaient autant de la réplication elle-même. Il me semblait que les rapports lisaient et que la réplication écrivait.

Qu'est-ce qui provoque un conflit de réplication ? Nous avons un maître où des processus s'exécutent. Nous avons un autovacuum. Que fait l'autovacuum ? Il supprime certaines anciennes lignes. Si, à ce moment-là, une requête sur le réplica lit ces anciennes lignes, et que sur le maître, l'autovacuum a marqué ces lignes comme pouvant être réécrites, nous les avons réécrites. Et nous recevons un paquet de données alors que nous devons réécrire les lignes nécessaires à la requête sur le réplica, alors le processus de réplication attendra le délai d'expiration que vous avez configuré. PostgreSQL décidera alors de ce qui est le plus important pour lui. Or, la réplication est plus importante pour lui que la requête, et il lancera la requête pour effectuer ces modifications sur le réplica.

Andrey, j'ai une question. Ces magnifiques graphiques que vous avez montrés lors de la présentation sont-ils le fruit de vos propres efforts ? Sur quoi les avez-vous construits ?

Ceci est un service Okmètre.

Est-ce un produit commercial ?

Oui, c'est un produit commercial.

Source: habr.com

Achetez un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Achetez un hébergement web fiable avec protection DDoS, serveurs VPS et VDS | ProHoster