PostgreSQL peut assez bien évoluer verticalement. Plus vous pouvez mettre de ressources (CPU, mémoire, disque) à la disposition de votre serveur PostgreSQL, mieux il peut fonctionner. Cependant, alors que certaines parties de Postgres peuvent automatiquement utiliser les ressources accrues, d'autres parties nécessitent des changements de configuration avant que des améliorations puissent être remarquées.
Lisez la suite pour en savoir plus sur la façon de vous assurer que PostgreSQL utilise pleinement le système sur lequel vous l'exécutez.
CPU
Ce qui évolue automatiquement
PostgreSQL a une architecture de processus traditionnelle, consistant en un masterprocess (appelé postmaster ) qui génère un nouveau processus (appelé backend ) pour chaque nouvelle connexion client. Cela signifie que s'il y a plus de cœurs de CPU disponibles, plus de processus peuvent s'exécuter simultanément, et donc les backends n'ont pas à lutter autant pour la disponibilité du CPU. Les requêtes liées au processeur se termineront plus rapidement.
Vous souhaiterez peut-être ajuster le nombre maximal de connexions simultanées autorisées à l'échelle du système, par base de données ou par utilisateur :
-- system level
ALTER SYSTEM SET max_connections = 200;
-- database level
ALTER DATABASE dbname CONNECTION LIMIT 200;
-- user level
ALTER ROLE username CONNECTION LIMIT 20;
afin que les applications malveillantes ne finissent pas par accaparer trop de connexions.
Ce qu'il faut peaufiner
Le serveur PostgreSQL peut créer un processus pour s'occuper des tâches de maintenance, comme le vide, la réplication, les abonnements (pour la réplication logique), etc. Le nombre de ces travailleurs n'est pas déterminé dynamiquement, mais simplement défini via la configuration et par défaut à 8.
Le paramètre de configuration de niveau supérieur pour le nombre de processus de travail est :
# typically specified in postgresql.conf
max_worker_processes = 16
Augmenter cette valeur peut entraîner une accélération des tâches de maintenance, des requêtes parallèles et de la création d'index.
Requêtes parallèles
À partir de la version 9.6, Postgres peut exécuter des requêtes en parallèle si le planificateur de requêtes décide que cela peut aider. L'interrogation parallèle implique de créer des nœuds de calcul, de répartir le travail entre eux, puis de collecter (rassembler) les résultats. Sous réserve de la limite globale de max_worker_processes
défini précédemment, Postgres déterminera le nombre de nœuds de calcul pouvant être générés pour une requête parallèle en fonction de la valeur de deux paramètres de configuration :
# the maximum number of workers that the system can
# support for parallel operations
max_parallel_workers = 8
# the maximum number of workers that can be started
# by a single Gather or Gather Merge node
max_parallel_workers_per_gather = 8
Si vous avez des processeurs inactifs et des requêtes parallélisables, l'augmentation de ces valeurs peut accélérer ces requêtes.
Création d'index parallèles
Dans Postgres 11, la prise en charge de la création parallèle d'index B-Tree a été ajoutée. Si vous créez régulièrement des index B-Tree ou que vous les REINDEXEZ, augmenter cette valeur peut vous aider :
# the maximum number of parallel workers that can be
# started by a single utility command
max_parallel_maintenance_workers = 8
Cela permettra à Postgres de générer ces nombreux travailleurs (sous réserve de la limite globale de max_worker_processes
) pour accélérer la création d'index B-Tree.
Réplication Logique
La réplication logique (disponible dans Postgres 10 et versions ultérieures) s'appuie sur les processus de travail côté abonnement pour récupérer les modifications auprès de l'éditeur. En demandant à Postgres de générer plus de travailleurs de réplication logiques, les modifications peuvent être récupérées et appliquées en parallèle, surtout s'il y a plus de tables. Ce paramètre de configuration augmente le nombre total de travailleurs de réplication :
# maximum number of logical replication workers
max_logical_replication_workers = 8
Dans la réplication en continu, vous pouvez démarrer une synchronisation avec une sauvegarde de base. Cependant, pour la réplication logique, les modifications doivent être introduites via le protocole de réplication lui-même, sur le réseau. Cela peut prendre du temps. Autoriser plus de travailleurs pendant la phase de synchronisation peut accélérer ce processus :
# basically the number of tables that are synced in
# parallel during initialization of subscription
max_sync_workers_per_subscription = 8
Auto-vide
Périodiquement, sur la base d'un tas de paramètres de configuration, Postgres générera un tas de travailleurs qui VACUUMERont les tables de la base de données. Ceci est bien sûr appelé autovacuum, et le nombre de travailleurs que le lanceur d'autovacuum génère à chaque fois peut être défini via le paramètre de configuration :
# the maximum number of autovacuum processes
autovacuum_max_workers = 8
Compression WAL
Si vous avez du CPU à revendre, vous pouvez échanger du CPU contre de la bande passante disque en compressant les pages qui sont écrites dans les fichiers WAL. Cela réduit la quantité de données à écrire sur le disque, au détriment de davantage de cycles CPU pour compresser les données. Cela réduit également la taille des données qui doivent être envoyées via le réseau pour la réplication en continu.
Pratiquement, les avantages de la compression WAL valent bien le surcoût très raisonnable. Pour l'activer, utilisez :
# compresses full page images written to WAL
wal_compression = on
Mémoire
Ce qui évolue automatiquement
Le système d'exploitation gère et utilise automatiquement la mémoire inutilisée par toute application pour mettre en cache les données lues et écrites récemment sur le disque. Cela accélère considérablement les applications gourmandes en disque, et certainement PostgreSQL.
Sous Linux, l'hôte le plus populaire pour Postgres, la taille du cache disque du système d'exploitation ne peut pas être définie par l'utilisateur. Sa gestion est interne à Linux. Sous la pression de la mémoire, il cédera la mémoire cache du disque aux applications.
Ce qu'il faut peaufiner
Planificateur de requêtes
Le planificateur de requêtes doit inclure la quantité de cache disque fournie par le système d'exploitation en tant que facteur dans ses estimations. Si vous avez réussi à augmenter considérablement le cache disque du système d'exploitation (en augmentant la mémoire disponible), l'augmentation de ce paramètre de configuration peut aider à améliorer les estimations du planificateur :
# the planner's assumption about the effective size
# of the disk cache that is available to a single query.
effective_cache_size = 64GB
Mémoire partagée
PostgreSQL utilise un ensemble de tampons partagés entre tous les travailleurs et processus principaux. Ceux-ci sont appelés tampons partagés , et la quantité de mémoire allouée aux tampons partagés est définie à l'aide du paramètre de configuration :
shared_buffers = 32GB
Tampons temporaires
Lorsque des tables temporaires sont accédées par une requête, des tampons sont alloués pour mettre en cache le contenu lu. La taille de ce tampon est définie à l'aide du paramètre de configuration :
# the maximum number of temporary buffers used
# by each database session
temp_buffers = 100MB
Si vous avez de la mémoire à revendre et que les requêtes utilisent beaucoup de tables temporaires, l'augmentation de cette valeur peut accélérer ces requêtes.
Mémoire de travail
La mémoire de travail est allouée localement et en privé par les backends. Il est utilisé pour effectuer des tris et des jointures sans avoir à créer dans des tables temporaires. L'augmentation de la valeur par défaut de 4 Mo peut permettre aux requêtes de se terminer plus rapidement lors de la création de tables temporaires :
# the amount of memory to be used by internal sort
# operations and hash tables before writing to temporary disk files
work_mem = 16MB
Opérations de maintenance
La mémoire utilisée par VACUUM, la création d'index et d'autres commandes de maintenance similaires sont contrôlées par le paramètre de configuration maintenance_work_mem
. L'augmentation de ce montant peut accélérer ces opérations, en particulier sur les index ou les tables qui doivent être recréés.
La mémoire utilisée par les travailleurs de l'autovacuum peut être extraite de la mémoire de travail de maintenance (en définissant autovacuum_work_mem = -1
) ou configuré indépendamment.
# the maximum amount of memory to be used by
# maintenance operations
maintenance_work_mem = 128MB
# maximum amount of memory to be used by each
# autovacuum worker process
autovacuum_work_mem = -1
Disque
Ce qui évolue automatiquement
Les disques peuvent être rendus plus gros, plus rapides ou plus simultanés. La taille du disque est la seule chose dont PostgreSQL n'a pas besoin d'être informé. Par défaut, PostgreSQL ne s'empêchera pas d'utiliser l'espace disque disponible. C'est généralement très bien.
Vous pouvez cependant limiter la taille totale des fichiers temporaires créés, afin de fournir une certaine protection contre les requêtes qui tentent de trier un milliard de lignes, etc. :
# the maximum amount of disk space that a process
# can use for temporary files
temp_file_limit = 500GB
Ce qu'il faut peaufiner
Concurrence
Les disques RAID et les systèmes de fichiers comme ZFS peuvent être configurés pour prendre en charge plus de simultanéité. C'est-à-dire que quelques lectures/écritures sur disque peuvent être traitées simultanément par de tels systèmes de fichiers en raison de la façon dont ils stockent ou gèrent les données en interne.
Vous pouvez laisser Postgres émettre plusieurs E/S disque simultanées, en utilisant ce paramètre de configuration :
# the number of concurrent disk I/O operations that
# PostgreSQL expects can be executed simultaneously
effective_io_concurrency = 4
Ceci n'est actuellement utilisé que par les analyses de tas bitmap.
Coût de page aléatoire
Le planificateur de requêtes Postgres suppose que les lectures séquentielles sont plus rapides que les lectures aléatoires. La vitesse exacte est une valeur que vous pouvez modifier. Par défaut, il suppose que les lectures aléatoires sont 4 fois plus coûteuses.
En fonction de la configuration de votre disque, de la charge de travail et de l'analyse comparative, si vous êtes sûr que les lectures aléatoires sont, disons, seulement deux fois plus coûteuses que les lectures séquentielles, vous pouvez le dire à Postgres :
# the planner's estimate of the cost of a disk page
# fetch that is part of a series of sequential fetches
seq_page_cost = 1
# the planner's estimate of the cost of a
# non-sequentially-fetched disk page
random_page_cost = 2
Tablespaces
Pour profiter de plusieurs disques qui ne sont pas montés comme un seul grand système de fichiers, vous pouvez utiliser des tablespaces. Avec les tablespaces, vous pouvez placer des tables ou indexer différents systèmes de fichiers. Cela peut améliorer la simultanéité et fournit un moyen simple de gérer la croissance des tables.
CREATE TABLESPACE disk2 LOCATION '/mnt/disk2/postgres';
En savoir plus sur les tablespaces ici.
Réseau
Le réseau est généralement la ressource la moins utilisée sur un serveur PostgreSQL et est rarement saturé. Si vous avez besoin d'évoluer, il est assez facile d'ajouter plus d'interfaces réseau chacune avec sa propre IP et que PostreSQL les écoute toutes :
listen_addresses = '10.1.0.10,10.1.0.11'
Les clients devront être équilibrés en charge sur toutes les adresses IP sur lesquelles Postgres écoute.
Autre
Il y a quelques autres paramètres de configuration qui peuvent être modifiés, dont la plupart utilisent plus de CPU et mémoire.
Opérations par partition
Postgres 10 a introduit le partitionnement des tables, qui a été amélioré dans Postgres 11. Certaines optimisations de requêtes sur les partitions ne sont pas activées par défaut, car elles peuvent entraîner une consommation plus élevée du processeur et de la mémoire. Ce sont :
# allow a join between partitioned tables to be
# performed by joining the matching partitions
enable_partitionwise_join = on
# allow grouping or aggregation on a partitioned
# tables performed separately for each partition
enable_partitionwise_aggregate = on