web-dev-qa-db-fra.com

Optimiser PostgreSQL pour des tests rapides

Je passe de SQLite à PostgreSQL pour une application typique Rails.

Le problème est que l'exécution des spécifications est devenue lente avec PG.
Sous SQLite, cela prenait environ 34 secondes, sur PG, environ 76 secondes, ce qui est plus de 2 fois plus lent .

Alors maintenant, je veux appliquer quelques techniques pour amener les performances des spécifications à égalité avec SQLite sans modification du code (idéalement en définissant simplement les options de connexion, ce qui n'est probablement pas possible).

Voici deux choses évidentes qui me viennent à l’esprit:

  • Disque RAM (une bonne configuration avec RSpec sur OSX serait bien de voir)
  • Tables non journalisées (peut-il être appliqué à l'ensemble de la base de données afin de ne pas modifier tous les scripts?)

Comme vous l'avez peut-être compris, la fiabilité et le reste ne m'intéressent pas (la DB n'est qu'un truc jetable ici).
Je dois tirer le meilleur parti du PG et le rendre aussi rapide que possible .

Meilleure réponse décrirait idéalement les astuces pour cela, la configuration et les inconvénients de ces astuces.

UPDATE: fsync = off + full_page_writes = off seul le temps a été réduit à environ 65 secondes (~ -16 secondes). Bon début, mais loin de l’objectif de 34.

UPDATE 2: I essayé d'utiliser RAM disque mais le gain de performance était dans une La marge d'erreur ne semble donc pas en valoir la peine.

UPDATE 3: * J'ai trouvé le plus gros goulot d'étranglement et maintenant mes spécifications sont aussi rapides que celles de SQLite.

Le problème était le nettoyage de la base de données ayant effectué la troncature . Apparemment, SQLite est trop rapide là-bas.

Pour le "réparer", j'ouvre une transaction avant chaque test et je l'annule à la fin.

Quelques chiffres pour environ 700 tests.

  • Troncature: SQLite - 34s, PG - 76s.
  • Transaction: SQLite - 17s, PG - 18s.

2x augmentation de la vitesse pour SQLite. Augmentation de la vitesse 4x pour PG.

194
Dmytrii Nagirniak

Tout d’abord, utilisez toujours la dernière version de PostgreSQL. Les améliorations de performances sont toujours à venir, alors vous perdez probablement votre temps si vous optimisez une ancienne version. Par exemple, PostgreSQL 9.2 améliore considérablement la vitesse de TRUNCATE et ajoute bien sûr des analyses avec index uniquement. Même les versions mineures doivent toujours être suivies; voir le version policy .

À ne pas faire

Do [~ # ~] pas [~ # ~] place un tablespace sur un disque RAM ou un autre stockage non durable .

Si vous perdez un espace de table, toute la base de données peut être endommagée et difficile à utiliser sans un travail important. Cela présente très peu d'avantages par rapport à l'utilisation de UNLOGGED tables et à beaucoup de RAM pour le cache de toute façon.

Si vous voulez vraiment un système basé sur un disque virtuel, initdb un tout nouveau cluster sur le disque virtuel en initdb créant une nouvelle instance PostgreSQL sur le disque virtuel, vous disposez donc d'une instance PostgreSQL entièrement disponible.

Configuration du serveur PostgreSQL

Lors du test, vous pouvez configurer votre serveur pour opération non durable mais plus rapide .

C’est l’une des seules utilisations acceptables du paramètre fsync=off dans PostgreSQL. Ce paramètre dit à PostgreSQL de ne pas s'embarrasser d'écritures ordonnées ni d'aucun autre problème de protection de l'intégrité des données et de sécurité contre les collisions, en lui donnant la permission de supprimer totalement vos données en cas de coupure de courant ou de panne du système d'exploitation.

Il va sans dire que vous ne devriez jamais activer fsync=off En production, sauf si vous utilisez Pg en tant que base de données temporaire pour les données que vous pouvez générer à nouveau ailleurs. Si et seulement si vous faites pour désactiver fsync, vous pouvez également activer full_page_writes , car cela ne sert plus à rien. Attention, fsync=off Et full_page_writes S'appliquent au niveau du cluster , ils affectent donc . ) toutes les bases de données de votre instance PostgreSQL.

Pour une utilisation en production, vous pouvez éventuellement utiliser synchronous_commit=off Et définir un commit_delay, Car vous bénéficierez des mêmes avantages que fsync=off Sans le risque de corruption de données géante. Vous avez une petite fenêtre de perte de données récentes si vous activez la validation async, mais c'est tout.

Si vous avez la possibilité de modifier légèrement le DDL, vous pouvez également utiliser les tables UNLOGGED de la page 9.1+ pour éviter complètement la journalisation des WAL et obtenir un gain de vitesse réel au prix de l'effacement des tables en cas de panne du serveur. Il n'y a pas d'option de configuration pour rendre toutes les tables non-connectées, elle doit être définie pendant CREATE TABLE. En plus d’être utile pour les tests, c’est pratique si vous avez des tables pleines de données générées ou non importantes dans une base de données qui, autrement, contiennent des éléments dont vous avez besoin pour être sûr.

Consultez vos journaux et voyez si vous recevez des avertissements concernant trop de points de contrôle. Si vous êtes, vous devriez augmenter votre checkpoint_segments . Vous voudrez peut-être également ajuster votre checkpoint_completion_target afin de faciliter les écritures.

Réglez shared_buffers Sur votre charge de travail. Cela dépend du système d'exploitation, dépend de ce qui se passe sur votre ordinateur et nécessite quelques essais et erreurs. Les valeurs par défaut sont extrêmement conservatrices. Vous devrez peut-être augmenter la limite maximale de mémoire partagée du système d'exploitation si vous augmentez shared_buffers Sur PostgreSQL 9.2 et au-dessous; 9.3 et versions ultérieures ont changé la façon dont ils utilisent la mémoire partagée pour éviter cela.

Si vous utilisez quelques connexions qui font beaucoup de travail, augmentez work_mem Pour leur donner plus RAM pour jouer avec des sortes, etc. Méfiez-vous de cela. Le paramètre work_mem Peut causer des problèmes de mémoire insuffisante, car il s’agit d’une sorte de tri par connexion et donc d’une requête peut avoir plusieurs tris imbriqués. Vous ne pouvez réellement doit augmenter work_mem si vous pouvez voir des tris déborder sur le disque dans EXPLAIN ou enregistrés avec le paramètre log_temp_files (recommandé), mais une valeur plus élevée peut également permettre à Pg de choisir des plans plus intelligents.

Comme le dit un autre poster, il est sage de placer le xlog et les principaux tableaux/index sur des disques durs séparés, si possible. Séparer des partitions est plutôt inutile, vous voulez vraiment des disques séparés. Cette séparation a beaucoup moins d’avantages si vous utilisez fsync=off Et presque pas si vous utilisez UNLOGGED tables.

Enfin, réglez vos requêtes. Assurez-vous que vos random_page_cost Et seq_page_cost Reflètent les performances de votre système, assurez-vous que votre effective_cache_size Est correct, etc. Utilisez EXPLAIN (BUFFERS, ANALYZE) pour examiner les plans de requête individuels, et activez le module auto_explain pour signaler toutes les requêtes lentes. Vous pouvez souvent améliorer considérablement les performances des requêtes en créant simplement un index approprié ou en modifiant les paramètres de coût.

Autant que je sache, il est impossible de définir une base de données ou un cluster entier comme UNLOGGED. Ce serait intéressant de pouvoir le faire. Pensez à demander sur la liste de diffusion PostgreSQL.

Réglage du système d'exploitation hôte

Vous pouvez également effectuer quelques réglages au niveau du système d'exploitation. La principale chose à faire est de convaincre le système d’exploitation de ne pas vider les écritures sur le disque de manière agressive, car vous ne vous souciez pas vraiment de savoir si elles le font ou non.

Sous Linux, vous pouvez contrôler ceci avec les paramètres dirty_* De sous-système de mémoire virtuelle , comme dirty_writeback_centisecs.

Le seul problème avec le réglage trop lent des paramètres d'écriture différée est qu'un vidage effectué par un autre programme peut entraîner le vidage de tous les tampons accumulés par PostgreSQL, causant ainsi des blocages importants alors que tout est bloqué en écriture. Vous pourrez peut-être résoudre ce problème en exécutant PostgreSQL sur un système de fichiers différent, mais certains vidages peuvent être au niveau du périphérique ou au niveau de l'hôte complet et non au niveau du système de fichiers, vous ne pouvez donc pas compter sur cela.

Ce réglage nécessite vraiment de manipuler les paramètres pour voir ce qui convient le mieux à votre charge de travail.

Sur les nouveaux noyaux, vous pouvez vous assurer que vm.zone_reclaim_mode Est défini sur zéro, car cela peut entraîner de graves problèmes de performances avec les systèmes NUMA (la plupart des systèmes de nos jours) en raison d'interactions avec la façon dont PostgreSQL gère shared_buffers. .

Requête et réglage de la charge de travail

Ce sont des choses qui nécessitent des changements de code; ils peuvent ne pas vous convenir. Certains sont des choses que vous pourriez être en mesure d'appliquer.

Si vous ne travaillez pas par lots dans des transactions plus importantes, commencez. De nombreuses petites transactions coûtent cher. Vous devez donc traiter en lots chaque fois que cela est possible et pratique. Si vous utilisez une validation asynchrone, cela est moins important mais reste fortement recommandé.

Dans la mesure du possible, utilisez des tables temporaires. Ils ne génèrent pas de trafic WAL, ils sont donc beaucoup plus rapides pour les insertions et les mises à jour. Parfois, il vaut la peine d'insérer une série de données dans une table temporaire, de la manipuler comme vous le souhaitez, puis de faire un INSERT INTO ... SELECT ... Pour la copier dans la table finale. Notez que les tables temporaires sont par session; si votre session se termine ou si vous perdez votre connexion, la table temporaire disparaît et aucune autre connexion ne peut voir le contenu des tables temporaires d'une session.

Si vous utilisez PostgreSQL 9.1 ou une version plus récente, vous pouvez utiliser UNLOGGED tables pour les données que vous pouvez vous permettre de perdre, comme l’état de la session. Celles-ci sont visibles à travers différentes sessions et préservées entre les connexions. Ils sont tronqués si le serveur ferme mal et ne peut pas être utilisé pour tout ce que vous ne pouvez pas recréer, mais ils sont parfaits pour les caches, les vues matérialisées, les tables d'état, etc.

En général, ne faites pas DELETE FROM blah;. Utilisez TRUNCATE TABLE blah; À la place; c'est beaucoup plus rapide lorsque vous videz toutes les lignes d'un tableau. Tronquez plusieurs tables en un seul appel TRUNCATE si vous le pouvez. Il y a une mise en garde si vous faites beaucoup de TRUNCATES de petites tables encore et encore; voir: vitesse de troncature Postgresql

Si vous n'avez pas d'index sur les clés étrangères, DELETEs impliquant les clés primaires référencées par ces clés étrangères sera terriblement lent. Assurez-vous de créer de tels index si vous vous attendez à DELETE à partir des tables référencées. Les index ne sont pas nécessaires pour TRUNCATE.

Ne créez pas d'index dont vous n'avez pas besoin. Chaque index a un coût de maintenance. Essayez d’utiliser un ensemble minimal d’index et de laisser les analyses d’index bitmap les combiner plutôt que de conserver un trop grand nombre d’index énormes et coûteux à plusieurs colonnes. Si des index sont requis, essayez d’abord de renseigner la table, puis créez des index à la fin.

Matériel

Avoir assez RAM pour contenir toute la base de données est un gain énorme si vous pouvez le gérer.

Si vous ne disposez pas de suffisamment de RAM, plus le stockage est rapide, mieux c'est. Même un disque SSD bon marché fait toute la différence par rapport à la rotation de Rust. Cependant, ne faites pas confiance aux disques SSD bon marché pour la production, ils ne sont souvent pas protégés contre les pannes et peuvent détruire vos données.

Apprentissage

Le livre de Greg Smith, PostgreSQL 9.0 High Performance reste pertinent malgré sa référence à une version un peu plus ancienne. Ce devrait être une référence utile.

Rejoignez la liste de diffusion générale PostgreSQL et suivez-la.

En train de lire:

268
Craig Ringer

Utilisez une autre disposition du disque:

  • disque différent pour $ PGDATA
  • disque différent pour $ PGDATA/pg_xlog
  • disque différent pour les fichiers tem (par base $ PGDATA/base // pgsql_tmp) (voir la remarque à propos de work_mem)

postgresql.conf peaufiner:

  • shared_memory: 30% de la RAM disponible, mais pas plus de 6 à 8 Go. Il semble préférable d'avoir moins de mémoire partagée (2 Go à 4 Go) pour les charges de travail intensives en écriture.
  • work_mem: principalement pour les requêtes sélectionnées avec des tris/agrégations. Ceci est défini par connexion et la requête peut allouer cette valeur plusieurs fois. Si les données ne tiennent pas, le disque est utilisé (pgsql_tmp). Cochez "expliquer analyser" pour voir combien de mémoire avez-vous besoin
  • fsync et synchronous_commit: les valeurs par défaut sont sûres, mais si vous pouvez tolérer la perte de données, vous pouvez les désactiver
  • random_page_cost: si vous avez un SSD ou une matrice RAID rapide, vous pouvez l'abaisser à 2.0 (RAID) ou même à la version inférieure (1.1) pour le SSD
  • checkpoint_segments: vous pouvez aller plus haut 32 ou 64 et changer le checkpoint_completion_target à 0.9. Une valeur inférieure permet une récupération plus rapide après un crash
9
mys