Optimiser PostgreSQL pour des tests rapides

203

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

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

Alors maintenant, je veux appliquer certaines techniques pour amener les performances des spécifications au même niveau que SQLite sans modification de code (idéalement simplement en définissant les options de connexion, ce qui n'est probablement pas possible).

Quelques choses évidentes du haut de ma tête sont:

  • Disque RAM (une bonne configuration avec RSpec sur OSX serait bien à voir)
  • Tables non journalisées (peut-il être appliqué sur toute la base de données pour que je n'aie pas à changer tous les scripts?)

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

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

MISE À JOUR: fsync = off + full_page_writes = offseulement réduit le temps à ~ 65 secondes (~ -16 secondes). Bon début, mais loin de l'objectif de 34.

MISE À JOUR 2: J'ai essayé d'utiliser un disque RAM mais le gain de performances se situait dans une marge d'erreur. Cela ne semble donc pas en valoir la peine.

MISE À JOUR 3: * J'ai trouvé le plus gros goulot d'étranglement et maintenant mes spécifications fonctionnent aussi vite que celles de SQLite.

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

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

Quelques chiffres pour environ 700 tests.

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

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

Dmytrii Nagirniak
la source
2
Je doute vraiment que vous le fassiez aller aussi vite que SQLite. SQLite avec un seul utilisateur est incroyablement rapide. La conception de SQLite est très rapide avec un faible nombre d'utilisateurs et évolue mal; La conception de Pg évolue bien mais n'est pas aussi rapide pour un travail en masse simple avec un seul utilisateur.
Craig Ringer
1
Je m'en rends compte, mais il y a un cas particulier pour lequel j'espère optimiser la PG (tests) afin qu'elle soit aussi rapide que possible. Cela ne me dérange pas d'être un peu plus lent là-bas, mais 2.2x est un peu trop lent. Tu vois ce que je veux dire?
Dmytrii Nagirniak
+1 Je serais très intéressé par les mises à jour sur l'approche du disque RAM si vous avez des résultats à ce sujet.
tscho
@tscho je vais certainement le poster ici. Mais j'ai besoin de temps car je travaille sur d'autres trucs et "recherche" les trucs PG en "arrière plan".
Dmytrii Nagirniak
l' insertion des données est-elle votre problème ou votre requête ? Ce n'est pas clair à partir de votre question.
a_horse_with_no_name

Réponses:

281

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

À ne pas faire

Ne placez PAS d' espace disque logique sur un disque RAM ou tout autre stockage non durable .

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

Si vous voulez vraiment un système basé sur ramdisk, initdbun tout nouveau cluster sur le ramdisk en initdbcréant une nouvelle instance PostgreSQL sur le ramdisk, vous avez donc une instance PostgreSQL complètement jetable.

Configuration du serveur PostgreSQL

Lors des tests, vous pouvez configurer votre serveur pour un fonctionnement non durable mais plus rapide .

C'est l'une des seules utilisations acceptables pour le fsync=offparamètre dans PostgreSQL. Ce paramètre indique à peu près à PostgreSQL de ne pas se soucier des écritures ordonnées ou de tout autre élément désagréable de protection de l'intégrité des données et de sécurité en cas de crash, ce qui lui permet de supprimer totalement vos données si vous perdez de l'énergie ou avez un crash du système d'exploitation.

Inutile de dire que vous ne devez jamais activer fsync=offen production à moins que vous n'utilisiez Pg comme base de données temporaire pour des données que vous pouvez recréer ailleurs. Si et seulement si vous faites pour désactiver fsync, vous pouvez également le full_page_writesdésactiver, car cela ne sert plus à rien. Méfiez-vous de cela fsync=offet full_page_writesappliquez-le au niveau du cluster , afin qu'ils affectent toutes les bases de données de votre instance PostgreSQL.

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

Si vous avez la possibilité de modifier légèrement la DDL, vous pouvez également utiliser les UNLOGGEDtables de la Pg 9.1+ pour éviter complètement la journalisation WAL et obtenir une augmentation de vitesse réelle 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 enregistrées, elle doit être définie pendant CREATE TABLE. En plus d'être bon pour les tests, cela est pratique si vous avez des tables remplies de données générées ou non importantes dans une base de données qui contient autrement des éléments dont vous avez besoin pour être en sécurité.

Vérifiez vos journaux et voyez si vous recevez des avertissements concernant trop de points de contrôle. Si vous l'êtes, vous devriez augmenter vos checkpoint_segments . Vous pouvez également ajuster votre checkpoint_completion_target pour lisser les écritures.

Ajustez shared_bufferspour s'adapter à votre charge de travail. Cela dépend du système d'exploitation, dépend de ce qui se passe avec votre machine et nécessite des essais et des erreurs. Les valeurs par défaut sont extrêmement conservatrices. Vous devrez peut-être augmenter la limite de mémoire partagée maximale du système d'exploitation si vous augmentez shared_bufferssur PostgreSQL 9.2 et inférieur; 9.3 et au-dessus ont changé la façon dont ils utilisent la mémoire partagée pour éviter cela.

Si vous utilisez seulement quelques connexions qui font beaucoup de travail, augmentez leur work_mempour leur donner plus de RAM pour jouer, etc. Attention, un work_memparamètre trop élevé peut provoquer des problèmes de mémoire insuffisante car il n'est pas par tri par connexion, donc une requête peut avoir plusieurs types imbriqués. Vous ne devez vraiment augmenter que work_memsi vous pouvez voir des tris se répandre sur le disque EXPLAINou se connecter avec le log_temp_filesparamètre (recommandé), mais une valeur plus élevée peut également permettre à Pg de choisir des plans plus intelligents.

Comme dit par une autre affiche ici, il est sage de mettre le xlog et les tables / index principaux sur des disques durs séparés si possible. Des partitions séparées sont assez inutiles, vous voulez vraiment des disques séparés. Cette séparation présente beaucoup moins d'avantages si vous utilisez fsync=offet presque aucune si vous utilisez des UNLOGGEDtables.

Enfin, ajustez vos requêtes. Assurez - vous que votre random_page_costet seq_page_costreflètent la performance de votre système, assurez -vous de votre effective_cache_sizeest correct, etc. Utilisez EXPLAIN (BUFFERS, ANALYZE)d'examiner les plans de requête individuels, et tourner le auto_explainmodule pour signaler toutes les requêtes lentes. Vous pouvez souvent améliorer considérablement les performances des requêtes simplement en créant un index approprié ou en modifiant les paramètres de coût.

AFAIK il n'y a aucun moyen de définir une base de données entière ou un cluster comme UNLOGGED. Ce serait intéressant de pouvoir le faire. Pensez à demander sur la liste de diffusion PostgreSQL.

Optimisation du système d'exploitation hôte

Vous pouvez également effectuer certains réglages au niveau du système d'exploitation. La principale chose que vous voudrez peut-être 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 vraiment pas de savoir quand / si elles arrivent sur le disque.

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

Le seul problème avec le réglage des paramètres d'écriture différée est trop lâche, c'est qu'un vidage par un autre programme peut entraîner le vidage de tous les tampons accumulés de PostgreSQL, provoquant de gros blocages pendant que tout bloque sur les écritures. Vous pouvez peut-être atténuer cela en exécutant PostgreSQL sur un autre système de fichiers, mais certaines vidanges peuvent être au niveau de l'appareil ou au niveau de l'hôte entier et non au niveau du système de fichiers, vous ne pouvez donc pas vous fier à cela.

Ce réglage nécessite vraiment de jouer avec les paramètres pour voir ce qui fonctionne le mieux pour votre charge de travail.

Sur les noyaux plus récents, vous pouvez vous assurer qu'il vm.zone_reclaim_modeest 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 des interactions avec la gestion de PostgreSQL shared_buffers.

Optimisation des requêtes et de la charge de travail

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

Si vous ne regroupez pas le travail en transactions plus importantes, commencez. Beaucoup de petites transactions sont chères, vous devriez donc grouper des trucs chaque fois que cela est possible et pratique. Si vous utilisez la validation asynchrone, cela est moins important, mais toujours 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, cela vaut la peine d'intégrer un tas de données dans une table temporaire, de les manipuler comme vous le souhaitez, puis INSERT INTO ... SELECT ...de les 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 des UNLOGGEDtableaux pour les données que vous pouvez vous permettre de perdre, comme l'état de la session. Ceux-ci sont visibles sur différentes sessions et préservés entre les connexions. Ils sont tronqués si le serveur s'arrête de manière impure, ils ne peuvent donc pas être utilisés 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, non DELETE FROM blah;. Utilisez TRUNCATE TABLE blah;plutôt; c'est beaucoup plus rapide lorsque vous videz toutes les lignes d'un tableau. Tronquez plusieurs tables en un seul TRUNCATEappel si vous le pouvez. Il y a cependant une mise en garde si vous faites beaucoup TRUNCATESde petites tables encore et encore; voir: Vitesse de troncature Postgresql

Si vous n'avez pas d'index sur les clés étrangères, les DELETEs impliquant les clés primaires référencées par ces clés étrangères seront horriblement lentes. Assurez-vous de créer de tels index si vous vous y attendez à DELETEpartir des tables référencées. Les index ne sont pas requis pour TRUNCATE.

Ne créez pas d'index dont vous n'avez pas besoin. Chaque indice a un coût de maintenance. Essayez d'utiliser un ensemble minimal d'index et laissez les analyses d'index bitmap les combiner plutôt que de maintenir trop d'index multicolonnes énormes et coûteux. Lorsque des index sont requis, essayez de remplir d'abord la table, puis créez des index à la fin.

Matériel

Avoir suffisamment de RAM pour contenir la base de données entière est une énorme victoire si vous pouvez la gérer.

Si vous n'avez pas assez de RAM, le stockage le plus rapide est le mieux. Même un SSD bon marché fait une énorme différence par rapport à la rouille en rotation. Ne faites pas confiance aux SSD bon marché pour la production, ils ne sont souvent pas sûrs et peuvent manger vos données.

Apprentissage

Le livre de Greg Smith, PostgreSQL 9.0 High Performance reste pertinent malgré une 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:

Craig Ringer
la source
10
Je peux également recommander PostgreSQL 9.0 High Performance par @GregSmith, c'est vraiment une excellente lecture. Le livre couvre tous les aspects de l'optimisation des performances de la disposition du disque à l'optimisation des requêtes et vous donne une très bonne compréhension des composants internes de PG.
tscho
10
Je n'ai pas publié de mise à jour du livre pour PostgreSQL 9.1, la seule version depuis sa publication, car il n'y avait pas suffisamment de changements liés aux performances dans 9.1 pour le justifier.
Greg Smith
3
Grande rédaction. Tout comme une petite mise à jour, «Vous devrez peut-être augmenter la limite maximale de mémoire partagée du système d'exploitation si vous augmentez shared_buffers» n'est plus vrai (pour la plupart des utilisateurs) sous PostgreSQL 9.3: postgresql.org/docs/9.3/static/release-9- 3.html # AEN114343
Gunnlaugur Briem
1
@brauliobo Mes tests font souvent de nombreux tx à un TPS élevé ... parce que j'essaie de simuler la production, y compris les charges de travail lourdes. Si vous voulez dire "test linéaire à connexion unique", je serais d'accord avec vous.
Craig Ringer
1
stackoverflow.com/questions/11419536/… DELETE peut être plus rapide que TRUNCATE pour les tables à quelques lignes, ce qui est probablement le cas dans les tests.
Jonathan Crosmer
9

Utilisez une disposition de disque différente:

  • disque différent pour $ PGDATA
  • disque différent pour $ PGDATA / pg_xlog
  • disque différent pour les fichiers tem (par base de données $ PGDATA / base // pgsql_tmp) (voir la note sur work_mem)

modifications de postgresql.conf:

  • 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 tri / agrégation. C'est par paramètre de connexion et la requête peut allouer cette valeur plusieurs fois. Si les données ne peuvent pas tenir, le disque est utilisé (pgsql_tmp). Cochez «expliquer analyser» pour voir la quantité de mémoire dont vous avez 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 désactiver puis désactiver
  • random_page_cost: si vous avez un SSD ou une matrice RAID rapide, vous pouvez le réduire à 2.0 (RAID) ou même plus bas (1.1) pour SSD
  • checkpoint_segments: vous pouvez aller plus haut 32 ou 64 et changer checkpoint_completion_target à 0.9. Une valeur inférieure permet une récupération plus rapide après un crash
mys
la source
4
Notez que si vous utilisez déjà fsync=off, mettre pg_xlog sur un disque séparé ne s'améliore plus beaucoup.
intgr
La valeur de 1,1 pour SSD semble très peu qualifiée. Je reconnais que c'est ce que certains professionnels ont aveuglément recommandé. Même les disques SSD sont beaucoup plus rapides pour les lectures séquentielles que les lectures aléatoires.
Acumenus
@ABB Oui, mais vous avez également des effets de mise en cache du tampon du système d'exploitation au travail. Tous ces params sont un peu agités de toute façon ...
Craig Ringer