Comment déterminer le juste équilibre entre la taille du pool et les connexions à la base de données dans PostgreSQL

14

Nous avons du mal à gérer le trafic pendant les heures de pointe vers notre serveur de base de données. Nous cherchons à améliorer le matériel (voir cette question à ce sujet ), mais nous voulons également travailler sur la configuration du pooling et le réglage du serveur.

L'application sur laquelle nous travaillons est un jeu multijoueur au tour par tour pour smartphones, où le backend se compose de Rails avec licorne et PostgreSQL 9.1 comme base de données. Nous avons actuellement 600 000 utilisateurs enregistrés et puisque l'état du jeu est stocké dans la base de données, plusieurs milliers d'écritures sont effectuées toutes les deux secondes. Nous avons analysé les fichiers journaux de PostgreSQL à l' aide de PgBadger et pendant les heures critiques, nous obtenons beaucoup de

FATAL: remaining connection slots are reserved for non-replication superuser connections

La solution naïve pour contrer ce problème serait d'augmenter max_connections (qui est actuellement de 100) dans postgresql.conf . J'ai lu http://wiki.postgresql.org/wiki/Number_Of_Database_Connections qui indique que ce n'est peut-être pas la bonne chose à faire. Dans l'article susmentionné, il est fait référence à la recherche du juste milieu entre max_connections et la taille du pool .

Que faire pour trouver ce sweet spot? Existe-t-il de bons outils pour mesurer les performances d'E / S pour différentes valeurs de max_connections et de taille de pool ?

Notre configuration actuelle est de 4 serveurs de jeu, chacun ayant 16 ouvriers licornes et une taille de pool de 5.

Voici les paramètres postgres non par défaut que nous utilisons:

version                      | PostgreSQL 9.1.5 on x86_64-unknown-linux-gnu,compiled by gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3, 64-bit
checkpoint_completion_target | 0.9
checkpoint_segments          | 60
checkpoint_timeout           | 6min
client_encoding              | UTF8
effective_cache_size         | 2GB
lc_collate                   | en_US.UTF-8
lc_ctype                     | en_US.UTF-8
log_destination              | csvlog
log_directory                | pg_log
log_filename                 | postgresql-%Y-%m-%d_%H%M%S.log
log_line_prefix              | %t
log_min_duration_statement   | 200ms
log_rotation_age             | 1d
log_rotation_size            | 10MB
logging_collector            | on
max_connections              | 100
max_stack_depth              | 2MB
server_encoding              | UTF8
shared_buffers               | 1GB
ssl                          | on
TimeZone                     | localtime
wal_buffers                  | 16MB
work_mem                     | 8MB
lorgartzor
la source
Êtes-vous la personne qui posait des questions à ce sujet sur la liste de diffusion au cours des dernières semaines? Si oui, je vais ajouter des liens de retour à cette discussion. Aussi: Quels sont le matériel et la configuration de votre serveur DB ? wiki.postgresql.org/wiki/Slow_Query_Questions . Incluez des paramètres autres que ceux par défaut: wiki.postgresql.org/wiki/Server_Configuration . Avez-vous lu wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server ? Travaillez-vous par lots dans des transactions plus importantes dans la mesure du possible? Utilisez-vous une couche de mise en cache et si oui, quoi? Utilisez-vous synchronous_commit = offou un commit_delay?
Craig Ringer
Vous avez donc un total de 20 connexions au serveur PostgreSQL? 5 par serveur de jeux? Avec ces 5 conns de chaque serveur de jeu partagés entre 16 employés de licorne?
Craig Ringer
Oh, enregistrez-vous des requêtes lentes? Si oui, quels sont vos points chauds? INSERTS simple ? À quoi ressemble votre schéma - est-il partitionné? Quelles sont les explain analyzeexemples de requêtes? Quelle est la fréquence de vos points de contrôle et combien de temps prennent-ils? (voir les options de consignation des points de contrôle). Et sérieusement, quelle est votre version de PostgreSQL ? (Mise à jour: Il semble que vous listiez votre matériel ici: dba.stackexchange.com/questions/28061/… )
Craig Ringer
Quoi qu'il en soit, pour le réglage de la taille du pool en particulier, les seules vraies réponses sont de configurer une mesure robuste de la charge et du débit du serveur de base de données, puis de commencer à ajuster de haut en bas jusqu'à ce que vous trouviez le point idéal.
Craig Ringer
@CraigRinger Non, je ne suis pas cette personne. Mais merci pour les backlinks! J'ai lu Tuning Your PostgreSQL server et j'ai suivi certains des conseils mentionnés. J'ai maintenant inclus les paramètres non par défaut. Nous cherchons maintenant à effectuer des transactions et des tests plus importantssynchronous_commit = off
lorgartzor

Réponses:

14

La réponse courte ici est "essais et erreurs guidés par des mesures de surveillance et de performances".

Il existe quelques règles générales qui devraient vous aider à trouver la zone vague dans laquelle vous devriez commencer, mais elles sont très générales. Les grandes lignes directrices «nombre de processeurs plus nombre de disques indépendants» sont souvent citées, mais ce n'est qu'un point de départ incroyablement grossier.

Ce que vous devez vraiment faire, c'est mettre en place des mesures de performances robustes pour votre application. Commencez à enregistrer les statistiques.

Il n'y a pas grand-chose en termes d'outils intégrés pour cela. Il y a des choses comme le check_postgresscript nagios , la journalisation du compteur de performances du système Cacti, le collecteur de statistiques PostgreSQL, etc. Malheureusement, vous devrez le faire vous-même. Pour le côté PostgreSQL, voir la surveillance dans le manuel PostgreSQL. Certaines options tierces existent, comme Postgres Enterprise Monitor d'EnterpriseDB .

Pour les mesures au niveau de l'application mentionnées ici, vous souhaiterez les enregistrer dans des structures de données partagées ou dans une base de données externe non durable comme Redis et les agréger soit au fur et à mesure que vous les enregistrez, soit avant de les écrire dans votre base de données PostgreSQL. Essayer de vous connecter directement à Pg faussera vos mesures avec la surcharge créée par l'enregistrement des mesures et aggravera le problème.

L'option la plus simple est probablement un singleton dans chaque serveur d'applications que vous utilisez pour enregistrer les statistiques des applications. Vous voulez probablement garder une mise à jour constante min, max, n, total et moyenne; de cette façon, vous n'avez pas à stocker chaque point de statistique, juste les agrégats. Ce singleton peut écrire ses statistiques agrégées sur Pg toutes les x minutes, un taux suffisamment bas pour que l'impact sur les performances soit minime.

Commencer avec:

  • Quelle est la latence des requêtes? En d'autres termes, combien de temps l'application prend-elle pour recevoir une demande du client jusqu'à ce qu'elle réponde au client. Enregistrez-les globalement sur une période de temps plutôt que sous forme d'enregistrements individuels. Regroupez-le par type de demande; disons, par page.

  • Quel est le délai d'accès à la base de données pour chaque requête ou type de requête que l'application exécute? Combien de temps faut-il pour demander à la base de données des informations / stocker des informations jusqu'à ce qu'elles soient terminées et pouvoir passer à la tâche suivante? Encore une fois, agrégez ces statistiques dans l'application et écrivez uniquement les informations agrégées dans la base de données.

  • Quel est votre débit? En x minutes, combien de requêtes de chaque classe principale exécutée par votre application sont traitées par la base de données?

  • Pour cette même plage de temps de x minutes, combien de demandes de clients y avait-il?

  • Échantillonnage toutes les quelques secondes et agrégation sur les mêmes fenêtres de x minutes dans la base de données, combien de connexions à la base de données y avait-il? Combien d'entre eux étaient inactifs? Combien étaient actifs? Dans les encarts? Mises à jour? sélectionne? supprime? Combien de transactions y a-t-il eu durant cette période? Voir la documentation du collecteur de statistiques

  • Encore une fois, l'échantillonnage et l'agrégation sur le même intervalle de temps, à quoi ressemblaient les mesures de performances du système hôte? Combien lisent et combien écrivent des E / S disque / seconde? Mégaoctets par seconde de lecture et d'écriture sur le disque? Utilisation du processeur? Charge moyenne? Utilisation de la RAM?

Vous pouvez maintenant commencer à vous renseigner sur les performances de votre application en corrélant les données, en les représentant graphiquement, etc. Vous commencerez à voir des modèles, à trouver des goulots d'étranglement.

Vous pouvez apprendre que votre système est limité INSERTet fonctionne UPDATEà des taux de transaction élevés, malgré des E / S de disque assez faibles en mégaoctets par seconde. Ce serait un indice dont vous avez besoin pour améliorer vos performances de vidage de disque avec un contrôleur RAID de mise en cache à écriture différée alimenté par batterie ou certains SSD de haute qualité protégés par l'alimentation. Vous pouvez également utiliser synchronous_commit = offsi vous pouvez perdre quelques transactions en cas de panne du serveur et / oucommit_delay , pour supprimer une partie de la charge de synchronisation.

Lorsque vous représentez vos transactions par seconde par rapport au nombre de connexions simultanées et que vous corrigez le taux de demande variable que l'application voit, vous pourrez avoir une meilleure idée de l'endroit où se situe votre zone de débit idéale.

Si vous n'avez pas de stockage à vidage rapide (BBU RAID ou SSD durables rapides), vous ne voudrez pas plus qu'un nombre assez petit de connexions d'écriture active, peut-être au plus 2x le nombre de disques que vous avez, probablement moins selon l'arrangement RAID , les performances du disque, etc. Dans ce cas, cela ne vaut même pas la peine d'essais et d'erreurs; il suffit de mettre à niveau votre sous-système de stockage vers un avec des vidages de disque rapides .

Voir pg_test_fsyncpour un outil qui vous aidera à déterminer si cela pourrait être un problème pour vous. La plupart des packages PostgreSQL installent cet outil dans le cadre de contrib, vous ne devriez donc pas avoir besoin de le compiler. Si vous obtenez moins de quelques milliers d'opérations / seconde, pg_test_fsyncvous devez d' urgence mettre à niveau votre système de stockage. Mon ordinateur portable équipé d'un SSD obtient de 5000 à 7000. Ma station de travail au travail avec une matrice RAID 10 à 4 disques de disques SATA à 7 200 tr / min et l'écriture directe (sans mise en cache en écriture) obtient environ 80 opérations / seconde f_datasync, jusqu'à 20 opérations / seconde pour fsync(); c'est des centaines de fois plus lent . Comparatif: ordinateur portable avec SSD vs station de travail avec RAID 10 à écriture immédiate (sans mise en cache en écriture). Le SSD de cet ordinateur portable est bon marché et je ne lui fais pas nécessairement confiance pour vider son cache d'écriture en cas de coupure de courant; Je garde de bonnes sauvegardes et je ne l'utiliserais pas pour les données qui me tiennent à cœur. Les SSD de bonne qualité fonctionnent aussi bien sinon mieux et sont durables en écriture.

Dans le cas de votre candidature, je vous conseille fortement de vous pencher sur:

  • Un bon sous-système de stockage avec des rinçages rapides. Je ne peux insister assez sur ce point. SSD de bonne qualité à sécurité intégrée et / ou contrôleur RAID avec cache de réécriture protégé par alimentation.
  • En utilisant UNLOGGED tableaux pour les données que vous pouvez vous permettre de perdre. Agrégez-le périodiquement dans des tables enregistrées. Par exemple, gardez les jeux en cours dans des tables non enregistrées et écrivez les scores dans des tables durables ordinaires.
  • Utilisant un commit_delay (moins utile avec un stockage à vidage rapide - indice)
  • Éteindre synchronous_commit pour les transactions que vous pouvez vous permettre de perdre (moins utile avec le stockage à vidage rapide - indice)
  • Tables de partitionnement, en particulier les tables où les données "vieillissent" et sont nettoyées. Au lieu de supprimer d'une table partitionnée, supprimez une partition.
  • Index partiels
  • Réduction du nombre d'index que vous créez. Chaque index a un coût d'écriture.
  • Travail en lots dans des transactions plus importantes
  • Utilisation de réplicas de secours à chaud en lecture seule pour supprimer la charge de lecture de la base de données principale
  • Utiliser une couche de mise en cache comme memcached ou redis pour les données qui changent moins souvent ou peuvent se permettre d'être périmées. Vous pouvez utiliser LISTENet NOTIFYpour effectuer l'invalidation du cache à l'aide de déclencheurs sur les tables PostgreSQL.

En cas de doute: http://www.postgresql.org/support/professional_support/

Craig Ringer
la source