J'ai une instance de PostgreSQL 9.2 s'exécutant sur RHEL 6.3, une machine à 8 cœurs avec 16 Go de RAM. Le serveur est dédié à cette base de données. Étant donné que le fichier postgresql.conf par défaut est plutôt conservateur en ce qui concerne les paramètres de mémoire, j'ai pensé que ce serait une bonne idée de permettre à Postgres d'utiliser davantage de mémoire. À ma grande surprise, suivre les conseils sur wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server a considérablement ralenti pratiquement toutes les requêtes que je lance, mais il est évidemment plus visible pour les requêtes plus complexes.
J'ai aussi essayé de lancer pgtune, ce qui a donné la recommandation suivante avec plus de paramètres ajustés, mais cela n'a rien changé. Il suggère des shared_buffers de 1/4 de la taille de la RAM, ce qui semble correspondre aux conseils donnés ailleurs (et sur PG wiki en particulier).
default_statistics_target = 50
maintenance_work_mem = 960MB
constraint_exclusion = on
checkpoint_completion_target = 0.9
effective_cache_size = 11GB
work_mem = 96MB
wal_buffers = 8MB
checkpoint_segments = 16
shared_buffers = 3840MB
max_connections = 80
J'ai essayé de réindexer l'ensemble de la base de données après avoir modifié les paramètres (utilisation reindex database
), mais cela n'a pas aidé non plus. J'ai joué avec shared_buffers et work_mem. En les modifiant progressivement à partir des valeurs par défaut très conservatrices (128 Ko / 1 Mo), les performances ont progressivement diminué.
J'ai couru EXPLAIN (ANALYZE,BUFFERS)
sur quelques requêtes et le coupable semble être que Hash Join est nettement plus lent. Ce n'est pas clair pour moi pourquoi.
Pour donner un exemple spécifique, j'ai la requête suivante. Il s'exécute en ~ 2100ms sur la configuration par défaut et ~ 3300ms sur la configuration avec des tailles de mémoire tampon plus grandes:
select count(*) from contest c
left outer join contestparticipant cp on c.id=cp.contestId
left outer join teammember tm on tm.contestparticipantid=cp.id
left outer join staffmember sm on cp.id=sm.contestparticipantid
left outer join person p on p.id=cp.personid
left outer join personinfo pi on pi.id=cp.personinfoid
where pi.lastname like '%b%' or pi.firstname like '%a%';
EXPLAIN (ANALYZE,BUFFERS)
pour la requête ci-dessus:
- Tampons par défaut: http://explain.depesz.com/s/xaHJ
- Tampons plus importants: http://explain.depesz.com/s/Plk
La question qui se pose est de savoir pourquoi la performance diminue lorsque j'augmente la taille de la mémoire tampon. La machine ne manque définitivement pas de mémoire. L'allocation si la mémoire partagée dans le système d'exploitation est ( shmmax
et shmall
) définie sur de très grandes valeurs, cela ne devrait pas poser de problème. Je ne reçois aucune erreur dans le journal Postgres non plus. J'utilise autovacuum dans la configuration par défaut, mais je ne pense pas que cela y soit pour quelque chose. Toutes les requêtes ont été exécutées sur le même ordinateur à quelques secondes d'intervalle, avec une configuration modifiée (et un redémarrage de la PG).
Edit: Je viens de découvrir un fait particulièrement intéressant: lorsque j’effectue le même test sur mon iMac de mi-2010 (OSX 10.7.5) également avec Postgres 9.2.1 et 16 Go de RAM, je n’éprouve pas le ralentissement. Plus précisément:
set work_mem='1MB';
select ...; // running time is ~1800 ms
set work_mem='96MB';
select ...' // running time is ~1500 ms
Lorsque je fais exactement la même requête (celle ci-dessus) avec exactement les mêmes données sur le serveur, je reçois 2100 ms avec work_mem = 1Mo et 3200 ms avec 96 MB.
Le Mac est équipé de disques SSD, donc il est compréhensible de le faire plus rapidement, mais il présente un comportement auquel je m'attendrais.
Voir également la discussion de suivi sur pgsql-performance .
la source
Réponses:
Tout d'abord, gardez à l'esprit que work_mem est une opération et qu'il peut donc devenir excessif assez rapidement. En général, si vous ne rencontrez pas de problèmes de ralentissement, je laisserais work_mem seul jusqu'à ce que vous en ayez besoin.
En regardant vos plans de requête, une chose qui me frappe est que les hits de la mémoire tampon sont très différents si vous regardez les deux plans, et que même les analyses séquentielles sont plus lentes. Je soupçonne que le problème a à voir avec la mise en cache à lecture anticipée et d'avoir moins d'espace pour cela. Cela signifie que vous polarisez la mémoire pour réutiliser les index et contre les tables de lecture sur disque.
D'après ce que j'ai compris, PostgreSQL se penchera sur le cache pour une page avant de le lire à partir du disque car il ne sait pas vraiment si le cache du système d'exploitation contiendra cette page. Comme les pages restent alors dans le cache et que ce dernier est plus lent que le cache du système d'exploitation, cela modifie les types de requêtes qui sont rapides par rapport aux types qui sont lents. En fait, en lisant les plans, mis à part les problèmes de work_mem, il semble que toutes les informations de votre requête proviennent du cache, mais la question est de savoir quel cache.
work_mem : combien de mémoire nous pouvons allouer pour un tri ou une opération de jointure associée. Il s’agit d’une opération à la fois, et non d’une instruction ou d’un back-end. Une requête complexe unique peut donc utiliser plusieurs fois cette quantité de mémoire. Il n’est pas clair que vous atteignez cette limite, mais il convient de le noter et d’en prendre conscience. Si vous l'augmentez trop, vous perdez de la mémoire qui pourrait être disponible pour le cache de lecture et les tampons partagés.
shared_buffers : quantité de mémoire à allouer à la file d'attente de pages PostgreSQL. Maintenant, idéalement, l'ensemble intéressant de votre base de données restera dans la mémoire mise en cache ici et dans les mémoires tampons de lecture. Cependant, cela permet de s'assurer que les informations les plus fréquemment utilisées dans tous les backends sont mises en cache et ne sont pas vidées sur le disque. Sous Linux, ce cache est nettement plus lent que le cache de disque du système d'exploitation, mais il offre des garanties que le cache de disque du système d'exploitation n'est pas transparent et qu'il est transparent pour PostgreSQL. C'est assez clairement où est votre problème.
Donc, ce qui se passe, c'est que lorsque nous avons une requête, nous vérifions d'abord les tampons partagés, car PostgreSQL ™ a une connaissance approfondie de ce cache, et recherchons les pages. S'ils ne sont pas présents, nous demandons au système d'exploitation de les ouvrir à partir du fichier. Si le système d'exploitation a mis le résultat en cache, il renvoie la copie mise en cache (cette procédure est plus rapide que les tampons partagés, mais Pg ne peut pas dire s'il est mis en cache ou non). disque, et le disque est beaucoup plus lent, donc PostgreSQL ne prend généralement pas cette chance). N'oubliez pas que cela affecte également l'accès aux pages aléatoire et séquentiel. Vous pouvez donc obtenir de meilleures performances avec des paramètres de shared_buffers plus bas.
Mon sens est que vous obtenez probablement de meilleures performances, ou du moins une plus grande cohérence, dans les environnements à haute concurrence avec des paramètres de shared_buffer plus importants. N'oubliez pas non plus que PostgreSQL ™ récupère cette mémoire et la conserve. Par conséquent, si d'autres éléments sont en cours d'exécution sur le système, les mémoires tampon de lecture contiendront les fichiers lus par d'autres processus. C'est un sujet très vaste et complexe. Des paramètres de mémoire tampon partagée plus grands offrent de meilleures garanties de performance, mais peuvent être moins performants dans certains cas.
la source
Outre l'effet apparemment paradoxal que l'augmentation
work_mem
des performances diminue ( @Chris pourrait avoir une explication), vous pouvez améliorer votre fonction d'au moins deux manières.LEFT JOIN
avecJOIN
. Cela pourrait confondre le planificateur de requêtes et mener à des plans inférieurs.pi.firstname
etpi.lastname
pour prendre en charge lesLIKE
recherches non ancrées . (Des modèles plus courts, comme ceux'%a%'
pris en charge, sont également pris en charge, mais un index ne sera probablement pas utile pour les prédicats non sélectifs.):Ou un index multicolonne:
Devrait rendre votre requête un peu plus rapide. Pour cela, vous devez installer le module supplémentaire pg_trgm . Détails sous ces questions connexes:
En outre, avez-vous essayé de définir
work_mem
localement - pour la transaction en cours uniquement ?Cela empêche les transactions simultanées de consommer davantage de RAM, voire de s’affamer mutuellement.
la source