Santé des index dans PostgreSQL vu par un développeur Java

Bonjour.

Je m'appelle Vania et je suis dĂ©veloppeur Java. Il se trouve que je travaille beaucoup avec PostgreSQL – je m'occupe de la configuration de la base de donnĂ©es, de l'optimisation de la structure, de la performance et je joue un peu au DBA pendant le week-end.

Récemment, j'ai remis en ordre plusieurs bases de données dans nos microservices et j'ai écrit une bibliothÚque Java pg-index-health, qui facilite ce travail, fait gagner du temps et aide à éviter certaines erreurs courantes commises par les développeurs. C'est justement de cette bibliothÚque dont je vais parler aujourd'hui.

Santé des index dans PostgreSQL vu par un développeur Java

Avertissement

La version principale de PostgreSQL avec laquelle je travaille est la 10. Toutes les requĂȘtes SQL que j'utilise ont Ă©galement Ă©tĂ© vĂ©rifiĂ©es sur la version 11. La version minimale prise en charge est 9.6.

Contexte

Tout a commencĂ© il y a presque un an avec une situation Ă©trange pour moi : la crĂ©ation concurrentielle d'un index a Ă©chouĂ© avec une erreur. L'index lui-mĂȘme est restĂ© dans un Ă©tat non valide dans la base. L'analyse des logs a montrĂ© un manque de temp_file_limit. Et c'est parti... En creusant un peu plus, j'ai dĂ©couvert un tas de problĂšmes dans la configuration de la base de donnĂ©es et, retroussant mes manches, avec des yeux pĂ©tillants, je me suis mis Ă  les corriger.

Le premier problÚme - la configuration par défaut

Peut-ĂȘtre que la mĂ©taphore de Postgres, qui peut ĂȘtre lancĂ© sur une cafetiĂšre, commence Ă  bien lasser tout le monde, mais... la configuration par dĂ©faut soulĂšve vraiment plusieurs questions. Au minimum, il faut faire attention Ă  maintenance_work_mem, temp_file_limit, statement_timeout et lock_timeout.

Dans notre cas maintenance_work_mem était par défaut 64 Mo, tandis que temp_file_limit environ 2 Go - nous manquions tout simplement de mémoire pour créer un index sur une grande table.

C'est pourquoi, dans pg-index-health j'ai rassemblé un certain nombre de paramÚtres clés, selon moi, qu'il vaut la peine de configurer pour chaque base de données.

Le deuxiÚme problÚme - les index dupliqués

Nos bases vivent sur des disques SSD, et nous utilisons HA-une configuration avec plusieurs centres de données, un hÎte principal et n-un nombre variable de répliques. L'espace disque est une ressource trÚs précieuse pour nous ; il est tout aussi important que la performance et la consommation de CPU. Donc, d'un cÎté, nous avons besoin d'index pour des lectures rapides, mais de l'autre, nous ne voulons pas voir d'index superflus dans la base de données, car ils consomment de l'espace et ralentissent la mise à jour des données.

Et donc, aprĂšs avoir restaurĂ© tous les index non valides et aprĂšs avoir regardĂ© les prĂ©sentations d'Oleg Bartunov, j'ai dĂ©cidĂ© de faire un « grand » nettoyage. Il s'est avĂ©rĂ© que les dĂ©veloppeurs n'aiment pas lire la documentation de la base de donnĂ©es. Ils n'aiment vraiment pas. Cela entraĂźne deux erreurs typiques : un index créé manuellement sur la clĂ© primaire et un index similaire « manuel » sur une colonne unique. Le fait est qu'ils ne sont pas nĂ©cessaires – Postgres s'en occupera tout seul. Ces index peuvent ĂȘtre supprimĂ©s sans hĂ©sitation, et un diagnostic a Ă©tĂ© mis en place pour cela. index_dupliquĂ©s.

Problùme trois – index qui se chevauchent

La plupart des dĂ©veloppeurs dĂ©butants crĂ©ent des index sur une seule colonne. Progressivement, aprĂšs avoir goĂ»tĂ© Ă  cette tĂąche, les gens commencent Ă  optimiser leurs requĂȘtes et Ă  ajouter des index plus complexes comprenant plusieurs colonnes. C'est ainsi qu'apparaissent les index sur les colonnes A, A+B, A+B+C etc. Les deux premiers de ces index peuvent ĂȘtre facilement supprimĂ©s, car ils sont des prĂ©fixes du troisiĂšme. Cela Ă©conomise Ă©galement une quantitĂ© considĂ©rable d'espace disque et un diagnostic est disponible pour cela. index_recroisĂ©s.

ProblĂšme quatre – clĂ©s Ă©trangĂšres sans index

Postgres permet de crĂ©er des contraintes de clĂ© Ă©trangĂšre sans spĂ©cifier d'index de support. Dans de nombreuses situations, cela n'est pas un problĂšme et peut mĂȘme ne pas se manifester... Jusqu'Ă  un certain moment...

C'Ă©tait notre cas : Ă  un certain moment, un job, s'exĂ©cutant selon un emploi du temps et nettoyant la base des commandes de test, a commencĂ© Ă  « saturer » notre hĂŽte master. Le processeur et l'IO s'envolaient, les requĂȘtes ralentissaient et Ă©taient interrompues par des timeouts, le service affichait des erreurs 500. Une analyse rapide pg_stat_activity a montrĂ© que les requĂȘtes du type :

supprimer de <table> où id dans (…)

Tout en ayant l'index sur id dans la table cible, il y avait naturellement peu de suppressions selon la condition. On aurait pensé que tout devait fonctionner, mais, hélas, ça ne fonctionnait pas.

Le merveilleux explain analyze est venu à la rescousse, et a révélé qu'en plus de la suppression des enregistrements dans la table cible, une vérification de l'intégrité référentielle était également effectuée, et sur l'une des tables associées, cette vérification tombait dans une analyse séquentielle en raison de l'absence de l'index approprié. Ainsi, le diagnostic clés_étrangÚres_sans_index.

Problùme cinq – valeur nulle dans les index

Par dĂ©faut, Postgres inclut les valeurs nulles dans les index btree, mais elles ne sont gĂ©nĂ©ralement pas nĂ©cessaires. C'est pourquoi je m'efforce de supprimer ces nulls (diagnostic index_avec_valeurs_nuls), en crĂ©ant des index partiels sur les colonnes nullable du type where is not null. De cette façon, j'ai rĂ©ussi Ă  rĂ©duire la taille de l'un de nos index de 1877 Mo Ă  16 Ko. Et dans l'un des services, la taille de la base de donnĂ©es a diminuĂ© au total de 16 % (soit 4,3 Go en chiffres absolus) en raison de l'exclusion des valeurs nulles des index. Une Ă©conomie d'espace disque colossale avec des amĂ©liorations assez simples. 🙂

ProblĂšme six – absence de clĂ©s primaires

En raison des particularités du mécanisme MVCC dans Postgres il est possible qu'une telle situation survienne, le gonflement, lorsque la taille de votre table augmente rapidement en raison d'un grand nombre d'enregistrements morts. Je pensais naïvement que cela ne nous arriverait pas et qu'avec notre base de données, cela ne se produirait pas, aprÚs tout, nous sommes, oh là là!!!, de bons développeurs... Comme j'étais naïf et stupide


Un beau jour, une migration a dĂ©cidĂ© de mettre Ă  jour tous les enregistrements dans une grande table souvent utilisĂ©e. Nous avons obtenu +100 Go Ă  la taille de la table sans raison. C'Ă©tait incroyablement frustrant, mais nos mĂ©saventures ne se sont pas arrĂȘtĂ©es lĂ . AprĂšs que l'autovacuum sur cette table a durĂ© 15 heures, il est devenu clair que l'espace physique ne reviendrait pas. Nous ne pouvions pas arrĂȘter le service et effectuer un VACUUM FULL, donc nous avons dĂ©cidĂ© d'utiliser pg_repack. Et lĂ , il s'est avĂ©rĂ© que pg_repack ne peut pas traiter les tables sans clĂ© primaire ou autre contrainte d'unicitĂ©, et notre table n'en avait pas. C'est ainsi qu'est nĂ©e le diagnostic tables_without_primary_key.

Dans la version de la bibliothĂšque 0.1.5 la possibilitĂ© de collecter des donnĂ©es sur le bloat des tables et des index a Ă©tĂ© ajoutĂ©e et d’y rĂ©agir Ă  temps.

ProblĂšmes sept et huit – manque d'index et index inutilisĂ©s

Les deux diagnostics suivants — tables_with_missing_indexes et unused_indexes – sont apparus dans leur version finale relativement rĂ©cemment. En fait, on ne pouvait pas simplement les ajouter comme ça.

Comme je l'ai dĂ©jĂ  Ă©crit, nous utilisons une configuration avec plusieurs rĂ©pliques, et la charge de lecture sur diffĂ©rents hĂŽtes est fondamentalement diffĂ©rente. Au final, cela crĂ©e une situation oĂč certaines tables et index sur certains hĂŽtes ne sont pratiquement pas utilisĂ©s, et pour l'analyse, il est nĂ©cessaire de collecter des statistiques depuis tous les hĂŽtes du cluster. Il est Ă©galement nĂ©cessaire de rĂ©initialiser les statistiques sur chaque hĂŽte du cluster, on ne peut pas le faire uniquement sur le maĂźtre.

Cette approche nous a permis d'économiser plusieurs dizaines de gigaoctets en supprimant des index qui n'étaient jamais utilisés, tout en ajoutant des index manquants sur des tables rarement utilisées.

En conclusion

Il va de soi que pour pratiquement tous les diagnostics, il est possible de configurer une liste d'exceptions. Cela permet d'implémenter rapidement des vérifications dans votre application, prévenant ainsi l'apparition de nouvelles erreurs, et de corriger progressivement les anciennes.

Certaines vĂ©rifications peuvent ĂȘtre effectuĂ©es directement dans les tests fonctionnels immĂ©diatement aprĂšs l'application des migrations de la base de donnĂ©es. Et c'est probablement l'une des fonctionnalitĂ©s les plus puissantes de ma bibliothĂšque. Vous pouvez consulter un exemple d'utilisation dans une dĂ©mo.

Les vĂ©rifications d'index non utilisĂ©s ou manquants, ainsi que celles concernant le bloat, ne sont pertinentes que sur une vĂ©ritable base de donnĂ©es. Les valeurs collectĂ©es peuvent ĂȘtre enregistrĂ©es dans ClickHouse ou envoyĂ©es Ă  un systĂšme de surveillance.

J'espÚre sincÚrement que pg-index-health sera utile et recherchée. Vous pouvez également contribuer au développement de la bibliothÚque en signalant des problÚmes rencontrés et en proposant de nouveaux diagnostics.

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster