Les performances de la base de données sont une préoccupation très importante lors de la maintenance de votre cluster de base de données, d'autant plus qu'il se développe au fil du temps. Cela est particulièrement vrai si votre application a commencé avec un faible trafic, passant ensuite à des charges de travail de lecture-écriture modérées ou lourdes.
La chose à retenir est qu'il n'y a pas de configuration parfaite sur laquelle vous pouvez compter pendant longtemps, car certaines charges de travail peuvent changer avec le temps.
Avec ClusterControl, la création ou le déploiement d'un nouveau cluster de bases de données PostgreSQL effectue une analyse de base telle que la vérification de vos ressources matérielles, puis applique le réglage automatique et définit les valeurs des paramètres réglables sélectionnés. Au fur et à mesure de l'évolution de PostgreSQL, de nombreux outils ont également été développés pour prendre en charge différentes configurations, en particulier pour l'équilibrage de charge.
Dans ce blog, nous examinerons l'importance de HAProxy et comment il peut contribuer à améliorer les performances. C'est un vieil outil, mais un puissant proxy et/ou équilibreur de charge qui prend en charge non seulement les serveurs de base de données, mais également les protocoles spécifiques aux applications réseau. HAProxy peut fonctionner via la couche quatre et la couche sept respectivement, selon le type d'installation en fonction de la configuration.
Réglage des performances PostgreSQL
L'un des principaux facteurs de performance pour PostgreSQL commence par le réglage des paramètres de base d'initdb aux valeurs des paramètres d'exécution. Cela doit être en mesure de gérer la charge de travail souhaitée conformément à vos certaines exigences. Avant de pouvoir prendre la route pour la fonctionnalité HAProxy pour PostgreSQL, votre serveur de base de données doit être stable et réglé sur les variables souhaitées. Prenons une liste de domaines pour PostgreSQL sur ce qui peut avoir un impact sur les performances de votre serveur de base de données.
Réglage pour une gestion de la mémoire réalisable
PostgreSQL est efficace et il est possible de s'exécuter efficacement avec aussi peu que 256 Mo de mémoire. La mémoire n'est pas chère, mais la plupart des ensembles de données sont inférieurs à 4 Go. Si vous disposez d'au moins 4 Go, votre ensemble de données actif peut rester dans le fichier et/ou le cache shared_buffer.
L'optimisation de votre PostgreSQL pour la gestion de la mémoire est l'une des choses les plus élémentaires que vous devez définir. Le définir correctement peut avoir un impact sur l'amélioration des performances de votre serveur de base de données. Bien que cela dépende du type de tables avec lesquelles vous jouez. De mauvaises requêtes et de mauvaises définitions de table peuvent également entraîner de mauvaises performances. Avec des index appropriés définis pour vos tables et avec des requêtes faisant référence à des index, les chances peuvent atteindre de 80 % à 100 % des requêtes peuvent être extraites de votre mémoire. Ceci surtout si le tampon d'index a la bonne valeur pour charger votre index défini sur vos tables. Examinons les paramètres généralement définis pour l'amélioration des performances.
- shared_buffers - PostgreSQL dimensionne son espace mémoire principal avec des shared_buffers. Le cache de travail de tous les tuples chauds (et entrées d'index) dans PostgreSQL. Ce paramètre définit la quantité de mémoire utilisée par le serveur de base de données pour les tampons de mémoire partagée. C'est un cache pré-alloué (tampons). Pour les systèmes basés sur Linux, il est idéal de définir le paramètre du noyau kernel.shmmax qui peut être défini de manière persistante via le fichier de configuration du noyau /etc/sysctl.conf.
- temp_buffers - Définit le nombre maximum de tampons temporaires utilisés pour chaque session. Ce sont des tampons de session locaux utilisés uniquement pour accéder aux tables temporaires. Une session affectera les tampons temporaires selon les besoins jusqu'à la limite donnée par temp_buffers.
- work_mem - La mémoire de travail disponible pour les opérations de travail (tris) avant que PostgreSQL ne permute. Ne pas définir globalement (postgresql.conf). Utilisez par transaction car cela peut être mauvais par requête, par connexion ou par tri. Il est recommandé d'utiliser EXPLAIN ANALYZE pour voir si vous débordez ou non.
- maintenance_work_mem - Spécifie la quantité de mémoire à utiliser pour les opérations de maintenance (VACUUM, CREATE INDEX, et ALTER TABLE … ADD FOREIGN KEY…)
Réglage pour une gestion de disque réalisable
Un certain nombre de paramètres d'exécution à définir ici. Listons-les :
- temp_file_limit - Spécifie la quantité maximale d'espace disque qu'une session peut utiliser pour les fichiers temporaires, tels que les fichiers temporaires de tri et de hachage, ou le fichier de stockage pour un curseur maintenu. Une transaction tentant de dépasser cette limite sera annulée.
- fsync - Si fsync est activé, PostgreSQL essaiera de s'assurer que les mises à jour sont écrites physiquement sur le disque. Cela garantit que le cluster de bases de données peut être restauré dans un état cohérent après une panne du système d'exploitation ou du matériel. Bien que la désactivation de fsync améliore généralement les performances, elle peut entraîner une perte de données en cas de panne de courant ou de plantage du système. Par conséquent, il est conseillé de désactiver fsync uniquement si vous pouvez facilement recréer l'intégralité de votre base de données à partir de données externes
- synchronous_commit - Utilisé pour imposer que le commit attendra que WAL soit écrit sur le disque avant de renvoyer un statut de réussite au client. Cette variable a des compromis entre performance et fiabilité. Si vous avez besoin de plus de performances, désactivez-le, ce qui signifie que lorsque le serveur tombe en panne, vous avez tendance à perdre des données. Sinon, si la fiabilité est importante, activez-la. Cela signifie qu'il y aura un intervalle de temps entre l'état de réussite et une écriture garantie sur le disque, ce qui peut avoir un impact sur les performances.
- checkpoint_timeout, checkpoint_completion_target - PostgreSQL écrit les modifications dans WAL, ce qui est une opération coûteuse. S'il écrit fréquemment des modifications dans WAL, cela peut avoir un impact négatif sur les performances. Alors, comment cela fonctionne, le processus de point de contrôle vide les données dans les fichiers de données. Cette activité est effectuée lorsque CHECKPOINT se produit et peut causer une énorme quantité d'IO. Tout ce processus implique des opérations coûteuses de lecture/écriture sur disque. Bien que vous (utilisateur administrateur) puissiez toujours émettre CHECKPOINT chaque fois que cela semble nécessaire ou l'automatiser en définissant les valeurs souhaitées pour ces paramètres. Le paramètre checkpoint_timeout est utilisé pour définir le temps entre les points de contrôle WAL. Un réglage trop bas réduit le temps de récupération après un crash, car davantage de données sont écrites sur le disque, mais cela nuit également aux performances, car chaque point de contrôle finit par consommer de précieuses ressources système. Le checkpoint_completion_target est la fraction de temps entre les points de contrôle pour l'achèvement du point de contrôle. Une fréquence élevée de points de contrôle peut avoir un impact sur les performances. Pour un point de contrôle fluide, checkpoint_timeout doit être une valeur faible. Sinon, le système d'exploitation accumulera toutes les pages sales jusqu'à ce que le ratio soit atteint, puis lancera une grande chasse d'eau.
Régler d'autres paramètres pour les performances
Certains paramètres améliorent et stimulent les performances dans PostgreSQL. Listons-les ci-dessous :
- wal_buffers - PostgreSQL écrit son enregistrement WAL (journal d'écriture anticipée) dans les tampons, puis ces tampons sont vidés sur le disque. La taille par défaut du tampon, définie par wal_buffers, est de 16 Mo, mais si vous avez beaucoup de connexions simultanées, une valeur plus élevée peut donner de meilleures performances.
- effective_cache_size - Effective_cache_size fournit une estimation de la mémoire disponible pour la mise en cache du disque. Il ne s'agit que d'une indication, pas de la taille exacte de la mémoire allouée ou du cache. Il n'alloue pas de mémoire réelle mais indique à l'optimiseur la quantité de cache disponible dans le noyau. Si la valeur de this est trop faible, le planificateur de requêtes peut décider de ne pas utiliser certains index, même s'ils seraient utiles. Par conséquent, la définition d'une valeur élevée est toujours bénéfique.
- default_statistics_target - PostgreSQL collecte les statistiques de chacune des tables de sa base de données pour décider comment les requêtes seront exécutées sur celles-ci. Par défaut, il ne collecte pas trop d'informations, et si vous n'obtenez pas de bons plans d'exécution, vous devez augmenter cette valeur, puis exécuter à nouveau ANALYZE dans la base de données (ou attendre l'AUTOVACUUM).
Efficacité des requêtes PostgreSQL
PostgreSQL possède une fonctionnalité très puissante pour optimiser les requêtes. Avec l'optimiseur de requête génétique intégré (connu sous le nom de GEQO). Il utilise un algorithme génétique qui est une méthode d'optimisation heuristique par recherche aléatoire. Ceci est appliqué lors de l'optimisation à l'aide de JOIN qui fournit une très bonne optimisation des performances. Chaque candidat dans le plan de jointure est représenté par une séquence dans laquelle joindre les relations de base. Il effectue au hasard une relation génétique en générant simplement une séquence de jointure possible mais au hasard.
Pour chaque séquence de jointure considérée, le code de planification standard est appelé pour estimer le coût d'exécution de la requête à l'aide de cette séquence de jointure. Ainsi, pour chacune des séquences JOIN, toutes ont leurs plans de balayage de relation initialement déterminés. Ensuite, le plan de requête calculera le plan le plus faisable et le plus performant, c'est-à-dire avec un coût estimé inférieur et sera considéré comme "plus adapté" que ceux avec un coût plus élevé.
Étant donné qu'il dispose d'une fonctionnalité puissante intégrée à PostgreSQL et des paramètres configurés appropriés conformément aux exigences souhaitées, il n'empêche pas la faisabilité en termes de performances si la charge n'est envoyée qu'à un nœud principal. L'équilibrage de charge avec HAProxy améliore encore les performances du disque pour PostgreSQL.
Améliorer les performances de PostgreSQL avec le fractionnement lecture-écriture
Vous pouvez avoir d'excellentes performances avec votre nœud de serveur PostgreSQL, mais vous ne pouvez peut-être pas anticiper le type de charge de travail que vous pourriez avoir, en particulier lorsque le trafic est élevé et que la demande dépasse les limites. L'équilibrage de la charge entre un primaire et un secondaire améliore les performances de votre application et/ou des clients se connectant à votre cluster de bases de données PostgreSQL. Comment cela peut être fait, n'est plus une question car c'est une configuration très courante pour une haute disponibilité et une redondance lorsqu'il s'agit de répartir la charge et d'éviter que le nœud principal ne s'enlise en raison d'un traitement à charge élevée.
La configuration avec HAProxy est simple. Pourtant, c'est plus efficace, plus rapide et faisable avec ClusterControl. Nous allons donc utiliser ClusterControl pour configurer cela pour nous.
Configurer PostgreSQL avec HAProxy
Pour ce faire, nous devrons simplement installer et configurer HAProxy au-dessus des clusters PostgreSQL. HAProxy a une fonctionnalité pour prendre en charge PostgreSQL via l'option pgsql-check mais sa prise en charge est une implémentation très simple pour déterminer si un nœud est actif ou non. Il n'a pas de contrôles pour identifier un nœud principal et un nœud de récupération. Une option consiste à utiliser xinetd pour lequel nous nous appuierons sur la communication du HAProxy pour écouter via notre service xinetd qui vérifie la santé d'un nœud particulier dans notre cluster PostgreSQL.
Sous ClusterControl, accédez à Gérer → Équilibreur de charge comme ci-dessous,
Ensuite, suivez simplement en fonction de l'interface utilisateur par capture d'écran ci-dessous. Vous pouvez cliquer sur Afficher les paramètres avancés pour afficher des options plus avancées. Suivre l'interface utilisateur est cependant très simple. Voir ci-dessous,
J'importe uniquement un nœud HAProxy sans redondance mais dans le but de ce blog, simplifions les choses.
Mon exemple de vue HAProxy est illustré ci-dessous,
Comme indiqué ci-dessus, les 192.168.30.20 et 192.168.30.30 sont les principaux et les nœuds secondaires/de récupération respectivement. Alors que le HAProxy est installé dans le nœud secondaire/de récupération. Idéalement, vous pouvez installer votre HAProxy sur plusieurs nœuds pour avoir plus de redondance et une haute disponibilité, il est préférable de l'isoler des nœuds de la base de données. Si votre budget est serré ou si vous économisez votre utilisation, vous pouvez choisir d'installer vos nœuds HAProxy, là où vos nœuds de base de données sont également installés.
ClusterControl le configure automatiquement et inclut également le service xinetd pour la vérification PostgreSQL. Cela peut être vérifié avec netstat comme ci-dessous,
[email protected]:~# netstat -tlv4np|grep haproxy
tcp 0 0 0.0.0.0:5433 0.0.0.0:* LISTEN 28441/haproxy
tcp 0 0 0.0.0.0:5434 0.0.0.0:* LISTEN 28441/haproxy
tcp 0 0 0.0.0.0:9600 0.0.0.0:* LISTEN 28441/haproxy
Alors que le port 5433 est en lecture-écriture et 5444 est en lecture seule.
Pour PostgreSQL, vérifiez le service xinetd, à savoir postgreshk, comme indiqué ci-dessous,
[email protected]:~# cat /etc/xinetd.d/postgreschk
# default: on
# description: postgreschk
service postgreschk
{
flags = REUSE
socket_type = stream
port = 9201
wait = no
user = root
server = /usr/local/sbin/postgreschk
log_on_failure += USERID
disable = no
#only_from = 0.0.0.0/0
only_from = 0.0.0.0/0
per_source = UNLIMITED
}
Les services xinetd s'appuient également sur /etc/services afin que vous puissiez trouver le port désigné pour mapper.
[email protected]:~# grep postgreschk /etc/services
postgreschk 9201/tcp
Si vous avez besoin de changer le port de votre postgreschk sur le port à mapper, vous devez également modifier ce fichier en dehors du fichier de configuration du service, puis n'oubliez pas de redémarrer le démon xinetd.
Le service postgreschk contient une référence à un fichier externe qui vérifie essentiellement les nœuds s'il est accessible en écriture, ce qui signifie qu'il s'agit d'un fichier primaire ou maître. Si un nœud est en récupération, il s'agit d'un réplica ou d'un nœud de récupération.
[email protected]:~# cat /usr/local/sbin/postgreschk
#!/bin/bash
#
# This script checks if a PostgreSQL server is healthy running on localhost. It will
# return:
# "HTTP/1.x 200 OK\r" (if postgres is running smoothly)
# - OR -
# "HTTP/1.x 500 Internal Server Error\r" (else)
#
# The purpose of this script is make haproxy capable of monitoring PostgreSQL properly
#
export PGHOST='localhost'
export PGUSER='s9smysqlchk'
export PGPASSWORD='password'
export PGPORT='7653'
export PGDATABASE='postgres'
export PGCONNECT_TIMEOUT=10
FORCE_FAIL="/dev/shm/proxyoff"
SLAVE_CHECK="SELECT pg_is_in_recovery()"
WRITABLE_CHECK="SHOW transaction_read_only"
return_ok()
{
echo -e "HTTP/1.1 200 OK\r\n"
echo -e "Content-Type: text/html\r\n"
if [ "$1x" == "masterx" ]; then
echo -e "Content-Length: 56\r\n"
echo -e "\r\n"
echo -e "<html><body>PostgreSQL master is running.</body></html>\r\n"
elif [ "$1x" == "slavex" ]; then
echo -e "Content-Length: 55\r\n"
echo -e "\r\n"
echo -e "<html><body>PostgreSQL slave is running.</body></html>\r\n"
else
echo -e "Content-Length: 49\r\n"
echo -e "\r\n"
echo -e "<html><body>PostgreSQL is running.</body></html>\r\n"
fi
echo -e "\r\n"
unset PGUSER
unset PGPASSWORD
exit 0
}
return_fail()
{
echo -e "HTTP/1.1 503 Service Unavailable\r\n"
echo -e "Content-Type: text/html\r\n"
echo -e "Content-Length: 48\r\n"
echo -e "\r\n"
echo -e "<html><body>PostgreSQL is *down*.</body></html>\r\n"
echo -e "\r\n"
unset PGUSER
unset PGPASSWORD
exit 1
}
if [ -f "$FORCE_FAIL" ]; then
return_fail;
fi
# check if in recovery mode (that means it is a 'slave')
SLAVE=$(psql -qt -c "$SLAVE_CHECK" 2>/dev/null)
if [ $? -ne 0 ]; then
return_fail;
elif echo $SLAVE | egrep -i "(t|true|on|1)" 2>/dev/null >/dev/null; then
return_ok "slave"
fi
# check if writable (then we consider it as a 'master')
READONLY=$(psql -qt -c "$WRITABLE_CHECK" 2>/dev/null)
if [ $? -ne 0 ]; then
return_fail;
elif echo $READONLY | egrep -i "(f|false|off|0)" 2>/dev/null >/dev/null; then
return_ok "master"
fi
return_ok "none";
La combinaison utilisateur/mot de passe doit être un ROLE valide sur votre serveur PostgreSQL. Puisque nous installons via ClusterControl, cela est géré automatiquement.
Maintenant que nous avons une installation HAProxy complète, cette configuration nous permet d'avoir un fractionnement en lecture-écriture où les lectures-écritures vont au nœud primaire ou inscriptible, alors qu'en lecture seule pour le primaire et le secondaire/ nœuds de récupération. Cette configuration ne signifie pas qu'elle est déjà performante, elle a toujours été ajustée comme indiqué précédemment avec une combinaison de HAProxy pour l'équilibrage de charge qui ajoute plus de performances pour votre application et les clients de base de données respectifs.