Vous venez de découvrir que votre serveur Linux possède des données dans son espace de swap et vous ne savez pas comment l’interpréter. Faut-il s’affoler, ou n’est-ce qu’une charge passagère qui date de l’année passée ? Ou pire encore, votre superviseur Nagios/Zabbix vient de vous remonter une alerte critique précisant que 90% de l’espace de swap sont occupés.
Cet article se propose de vous aider à comprendre la différence entre pagination et swap, l’impact réel sur le système, vos bases de données et vos applications, puis à élaborer une stratégie de configuration adaptée à vos objectifs et votre environnement qu’il soit ou non de production.
Les exemples cités ci-après correspondent à des contextes de serveur hébergeant des bases MySQL, mais la démarche serait similaire avec des bases Oracle. Et il est fort probable que tout DBA Oracle sera à même de traduire aisément la troisième étape de cet article plus spécifique aux bases MySQL, à son contexte équivalent sous Oracle.
Six étapes pour y voir plus clair :
1. La première étape consiste à déterminer l’ampleur du problème
2. La deuxième étape permet d’identifier les process affectés
3. La troisième étape vérifie la configuration des bases MySQL
4. La quatrième étape tente de limiter l’activité de swapping
5. La cinquième étape aide à protéger au mieux les bases MySQL de production
6. Point particulier : les architectures NUMA
1. Déterminer l’ampleur du problème
L’évolution de l’occupation de l’espace de swap dans les jours passés sera la première base d’analyse.
- Notez l’espace occupé dans l’espace de swap actuellement sur votre système :
# free -h # ou -m pour un résultat en Mo total used free shared buffers cached Mem: 15G 14G 1.4G 40K 225M 10G -/+ buffers/cache: 3.1G 12G Swap: 511M 442M 69M
ainsi que l’organisation physique de cet espace de swap et son éventuelle répartition sur plusieurs partitions :
[root@mysqlserver ~]# cat /proc/swaps # idem swapon -s Filename Type Size Used Priority /dev/dm-1 partition 524284 442136 -1 /dev/dm-8 partition 2621436 764972 -2
Ci-dessus, il est probable que la seconde partition (dm-8) ait été ajoutée à chaud par un ingénieur système suite à un problème (c’est la taille de cette partition qui me fait penser cela).
Si cette intervention est récente, il est possible d’en retrouver une trace dans un des fichiers logs du système (messages, dmesg, syslog
ou encorekern.log
sous/var/log
). Tout dépendra de la configuration de la rotation (/etc/logrotate.conf
).
Et si l’intervention est très récente – moins d’un mois – vous retrouverez même à quelle heure précise elle a eu lieu grâce aux logs présentés ci-dessous. - En effet, sur les serveurs Linux, il est possible à posteriori d’analyser la fréquence de la pagination/swapping heure par heure, car deux commandes
sar (System Activity Report)
sont lancées périodiquement, l’une une fois par heure avec toutes les options desar
, l’autre toutes les 10mn.[root@mysqlserver ~]# cat /etc/cron.d/sysstat # Run system activity accounting tool every 10 minutes */10 * * * * root /usr/lib64/sa/sa1 1 1 # 0 * * * * root /usr/lib64/sa/sa1 600 6 & # Generate a daily summary of process accounting at 23:53 53 23 * * * root /usr/lib64/sa/sa2 -A
Cette configuration a été mise en place en
crontab
lors de l’installation du packagesysstat
. Si le package n’est pas déjà installé sur votre serveur, ce qui serait tout de même surprenant, il vous suffira de lancer :[root@mysqlserver ~]# yum install sysstat
et si vous êtes sous RedHat 7 ou une Debian assez récente, vous ajouterez :
[root@mysqlserver ~]# systemctl start sysstat [root@mysqlserver ~]# systemctl status sysstat
et uniquement si la seconde ligne du résultat du
status
précédent ne se termine pas par «enable
) », activez son lancement automatique à chaque reboot par :[root@mysqlserver ~]# systemctl enable sysstat
Les résultats des
sar
sont conservés dans un fichier quotidien sur 28 jours glissants. Le nom du fichier reprend le numéro du jour du mois.
Pour être plus précis, deux fichiers sont générés par jour, l’un étant le binaire de l’autre. Par exemple, pour le 19 du mois, le fichiersa19
est le binaire du fichier textesar19
. Mais peu importe, les bouclesfor
ci-dessous vous permettront d’exploiter ces fichiers au mieux sans vous poser trop de questions.
Si vous souhaitez modifier la durée de conservation des logssar
, il vous suffit de modifier la variableHISTORY
du fichier de configuration /etc/sysconfig/sysstat
, après avoir lu le petit commentaire qui lui précède 😉 puis de demander au démon de prendre en compte votre modification par la commande :[root@mysqlserver ~]# systemctl reload sysstat
Ces différentes syntaxes de la commande
systemctl
vous permettent de découvrir ainsi le côté simple et sympathique desystemd
. - Avant de lancer une commande pour exploiter les logs des
sar
, vérifiez la présence de ces logs et éventuellement notez les plus récents :[root@mysqlserver ~]# function l { ls -alt "$@" | less -eFmRX ; } # ~/.bashrc ? [root@mysqlserver ~]# l /var/log/sa/ -rw-r--r--. 1 root root 899124 Oct 19 23:53 sar19 -rw-r--r--. 1 root root 830484 Oct 19 23:50 sa19 -rw-r--r--. 1 root root 899124 Oct 18 23:53 sar18 -rw-r--r--. 1 root root 830484 Oct 18 23:50 sa18 ...
Maintenant, il vous suffit de préciser les numéros des jours que vous souhaitez étudier dans les exemples de boucles for
ci-dessous à la place des 19
et 20
de la première ligne.
Dans les résultats que vous obtiendrez, le constat d’une progression régulière (colonnes pswpout/s
et kbswpused
) de seulement quelques pages, dénote d’une simple pagination et non d’un « swapping » violent et pénalisant.
- Par exemple, pour lister l’activité (
-W
: activité swap-S
: espace de swap-r
: mémoire-u
: CPU) sur deux jours précis (et sur la tranche horaire 9h-11h pour limiter le nombre de lignes du résultat ci-dessous) :for J in 19 20 do echo -e "\n---------------------------\n Le $J \n---------------------------" sar -WSru -f /var/log/sa/sa"$J" -s 09:00:00 -e 11:00:00 done | less
Notez dans le listing ci-dessous que le 19 à 10H20 MySQL a été relancée, d’où les pages de swapin (remontée du swap vers la RAM) .
Notez aussi que la colonne %memused ne décroche pas des 99% !for J in 19 20 Le 19 --------------------------- Linux 2.6.32-431.29.2.el6.x86_64 (HVMP1509) 10/19/2016 _x86_64_ (8 CPU) 09:00:01 AM CPU %user %nice %system %iowait %steal %idle 09:10:01 AM all 15.30 0.86 1.60 0.94 0.00 81.30 09:20:01 AM all 19.91 0.00 1.38 0.97 0.00 77.74 09:30:01 AM all 19.19 0.00 1.49 0.62 0.00 78.70 09:40:01 AM all 14.26 0.00 1.26 0.79 0.00 83.69 09:50:01 AM all 14.75 0.00 1.26 1.01 0.00 82.98 10:00:01 AM all 14.03 0.00 1.14 1.13 0.00 83.69 10:10:01 AM all 15.64 0.00 1.31 0.92 0.00 82.12 10:20:01 AM all 21.96 0.00 2.31 2.87 0.00 72.86 10:30:01 AM all 18.15 0.00 1.58 1.35 0.00 78.92 10:40:01 AM all 27.00 0.00 2.23 1.01 0.00 69.76 10:50:02 AM all 29.91 0.00 2.29 0.72 0.00 67.08 Average: all 19.08 0.08 1.62 1.12 0.00 78.10 09:00:01 AM pswpin/s pswpout/s 09:10:01 AM 0.07 0.00 09:20:01 AM 0.01 0.00 09:30:01 AM 0.00 0.00 09:40:01 AM 0.00 0.80 09:50:01 AM 0.01 2.50 10:00:01 AM 0.00 0.44 10:10:01 AM 1.27 0.08 10:20:01 AM 152.08 0.67 10:30:01 AM 0.13 0.36 10:40:01 AM 0.14 0.30 10:50:02 AM 0.09 0.76 Average: 13.88 0.54 09:00:01 AM kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit 09:10:01 AM 776312 32096240 97.64 44692 20491224 19062152 54.51 09:20:01 AM 483676 32388876 98.53 76364 20754124 19062264 54.51 09:30:01 AM 190600 32681952 99.42 96464 21021320 19062128 54.51 09:40:01 AM 192228 32680324 99.42 112396 20999268 19062132 54.51 09:50:01 AM 134004 32738548 99.59 125872 21051188 19062132 54.51 10:00:01 AM 134868 32737684 99.59 148340 21022664 19062128 54.51 10:10:01 AM 135572 32736980 99.59 160976 21000248 19080660 54.56 10:20:01 AM 135752 32736800 99.59 175108 20542328 19028484 54.41 10:30:01 AM 292240 32580312 99.11 183384 20338208 19022076 54.40 10:40:01 AM 136280 32736272 99.59 201608 20470692 19022072 54.40 10:50:02 AM 311068 32561484 99.05 210036 20290640 19022044 54.40 Average: 265691 32606861 99.19 139567 20725628 19049843 54.48 09:00:01 AM kbswpfree kbswpused %swpused kbswpcad %swpcad 09:10:01 AM 298452 1798696 85.77 58476 3.25 09:20:01 AM 298464 1798684 85.77 58476 3.25 09:30:01 AM 298464 1798684 85.77 58476 3.25 09:40:01 AM 296544 1800604 85.86 58348 3.24 09:50:01 AM 290592 1806556 86.14 58440 3.23 10:00:01 AM 289544 1807604 86.19 58116 3.22 10:10:01 AM 290332 1806816 86.16 60124 3.33 10:20:01 AM 1256076 841072 40.11 93152 11.08 10:30:01 AM 1255320 841828 40.14 93496 11.11 10:40:01 AM 1254740 842408 40.17 93548 11.10 10:50:02 AM 1253020 844128 40.25 93784 11.11 Average: 643777 1453371 69.30 71312 4.91 --------------------------- Le 20 --------------------------- Linux 2.6.32-431.29.2.el6.x86_64 (HVMP1509) 10/20/2016 _x86_64_ (8 CPU) 09:00:01 AM CPU %user %nice %system %iowait %steal %idle 09:10:01 AM all 15.39 0.85 1.45 1.02 0.00 81.29 09:20:01 AM all 16.48 0.00 0.99 1.03 0.00 81.50 09:30:01 AM all 12.41 0.00 1.08 0.99 0.00 85.51 09:40:02 AM all 12.49 0.00 1.05 0.81 0.00 85.66 09:50:01 AM all 12.05 0.00 0.99 0.76 0.00 86.20 10:00:01 AM all 12.36 0.00 1.04 0.75 0.00 85.85 10:10:01 AM all 11.89 0.00 1.03 1.20 0.00 85.88 10:20:02 AM all 11.84 0.00 1.00 0.75 0.00 86.42 10:30:01 AM all 13.38 0.00 1.13 0.77 0.00 84.72 10:40:01 AM all 13.57 0.00 1.17 0.76 0.00 84.50 10:50:01 AM all 15.42 0.00 1.24 0.81 0.00 82.53 Average: all 13.39 0.08 1.10 0.88 0.00 84.55 09:00:01 AM pswpin/s pswpout/s 09:10:01 AM 0.19 0.05 09:20:01 AM 0.39 0.00 09:30:01 AM 0.08 0.79 09:40:02 AM 0.09 0.19 09:50:01 AM 0.00 0.28 10:00:01 AM 0.00 0.23 10:10:01 AM 0.01 0.25 10:20:02 AM 0.00 0.26 10:30:01 AM 0.00 0.22 10:40:01 AM 0.00 0.33 10:50:01 AM 0.00 0.42 Average: 0.07 0.28 09:00:01 AM kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit 09:10:01 AM 449936 32422616 98.63 43788 20000932 19018168 54.38 09:20:01 AM 203072 32669480 99.38 73292 20217384 19018156 54.38 09:30:01 AM 158904 32713648 99.52 104844 20229124 19018128 54.38 09:40:02 AM 137276 32735276 99.58 120940 20233156 19018128 54.38 09:50:01 AM 136096 32736456 99.59 133436 20225648 19018128 54.38 10:00:01 AM 173368 32699184 99.47 145904 20177664 19018132 54.38 10:10:01 AM 135512 32737040 99.59 169808 20189536 19018120 54.38 10:20:02 AM 154820 32717732 99.53 180640 20165360 19018152 54.38 10:30:01 AM 178324 32694228 99.46 189004 20133076 19018156 54.38 10:40:01 AM 159396 32713156 99.52 197744 20142368 19018160 54.38 10:50:01 AM 171380 32701172 99.48 210940 20118624 19018128 54.38 Average: 187099 32685453 99.43 142758 20166625 19018141 54.38 09:00:01 AM kbswpfree kbswpused %swpused kbswpcad %swpcad 09:10:01 AM 1194036 903112 43.06 84020 9.30 09:20:01 AM 1194408 902740 43.05 84576 9.37 09:30:01 AM 1192520 904628 43.14 84144 9.30 09:40:02 AM 1192068 905080 43.16 84376 9.32 09:50:01 AM 1191400 905748 43.19 84324 9.31 10:00:01 AM 1190844 906304 43.22 84300 9.30 10:10:01 AM 1190256 906892 43.24 84376 9.30 10:20:02 AM 1189656 907492 43.27 84344 9.29 10:30:01 AM 1189120 908028 43.30 84600 9.32 10:40:01 AM 1188336 908812 43.34 84348 9.28 10:50:01 AM 1187344 909804 43.38 84472 9.28 Average: 1190908 906240 43.21 84352 9.31
- Version moins verbeuse pour une analyse rapide dès lors que vous connaissez déjà votre serveur :
for J in 19 20 do echo -e "\n---------------------------\n Le $J \n---------------------------" sar -WS -f /var/log/sa/sa"$J" done | less
- Version pour balayer tous les fichiers logs de sar :
cd /var/log/sa/ ; for J in $(ls -tr sa[0-9][0-9]) do echo -e "\n---------------------------\n Le $J \n---------------------------" sar -WS -f "$J" done | less
Pour compléter votre analyse, vous trouverez ci-dessous quelques commandes et options utiles.
- En premier lieu, et c’est de loin la commande la plus importante pour vérifier l’utilisation de la mémoire du système, car un problème de swap est avant tout un manque de mémoire détecté par votre noyau :
$ cat /proc/meminfo
Chaque ligne du résultat a son importance qui dépend de votre niveau d’expérience du système Linux. Ce résultat vient compléter celui de la commande
free
citée en tête de cet article.
Si la valeur du cache du système vous semble trop importante et vous perturbe, il est possible de libérer la plus grande de cette partie de la mémoire en toute sécurité par la commande suivante avant d’observer à nouveau le comportement de votre système (mais en général un DBA n’aime pas trop libérer la mémoire cache 😉 ).
Opération sans risque car elle ne libère pas la mémoire cache en cours d’utilisation par des process, juste celle inutilisée.# sync && sync && echo 1 > /proc/sys/vm/drop_caches
- Le système conserve aussi des statistiques globales. Ce sont des totaux depuis le dernier boot du serveur.
Se faire une idée par deux prises de valeurs à une journée ou une heure d’intervalle :$ vmstat -s
Observez les lignes
"pages paged in"
, «pages paged out
« , «pages swapped in
» et «pages swapped out
» qui font ressortir plus clairement les notions de pagination et de swap.
Lorsqu’une commande renvoie un nombre de pages mémoire, il vous faut multiplier cette valeur le plus souvent par 4 pour obtenir un résultat en Ko. La commande getconf vous donnera la taille d’une page mémoire :$ getconf PAGE_SIZE # renvoie la taille en octet d'une page mémoire
- Vous pouvez observer l’activité en temps réel des pages mémoire qui entrent et sortent de votre espace de swap :
Activité de pagination par seconde sur 7 secondes$ sar -W 1 7
- ou encore par l’intermédiaire de la commande
vmstat
qui donne aussi l’activité globale de votre système (plus difficile à interpréter si vous découvrez cette commande)$ vmstat -w 1 7 # certains se demandent encore pourquoi -w n'est pas par défaut
- et même si ce n’est pas directement lié à l’activité mémoire, il peut être judicieux d’observer l’activité disque par process (du même genre que la commande
top
mais dédiée aux I/O)$ iotop -o -d 0.5 # rafraîchir toutes les demi-secondes (utiliser ensuite les flèches droite et gauche)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2. Déterminer quels sont les process affectés
Le but est de vérifier si ce sont bien les bases MySQL qui sont à l’origine de la consommation de la mémoire puis d’éviter que les démons mysqld deviennent candidats au kill du Killer OOM (Out Of Memory), et plus particulièrement pour les bases de production.
Le Killer OOM est une fonction du noyau Linux chargée d’arrêter un ou plusieurs process lorsque le système vient à manquer cruellement de mémoire.
Le plus souvent le Killer OOM choisira le process le plus consommateur de RAM (ce n’est pas le seul critère pris en compte mais souvent le plus déterminant dans la consommations des ressources).
La trace de son passage se retrouvera dans les logs du noyau par un message du type :
horodate kernel: Out of memory: Kill process 3441 (mysqld) score 433 or sacrifice child
- Pour analyser l’occupation de l’espace de swap par les 7 process les plus gourmands :
# printf "SwapUsed\tName\tTgid Pid\n" for file in /proc/[0-9][0-9]*/status do awk 'BEGIN { pname=" " ; tgid=0 ; pid=0 } /Name/ { pname=$2 } /Tgid/ { tgid=$2 } /PidName/ { pid=$2 } $1 ~ /^VmSwap.*/ { print $2,$3 "\t" pname, tgid } ' "$file" done | sort -n | tail -7 ; printf "SwapUsed\tName\tTgid Pid\n"
SwapUsed Name Tgid Pid 2728 kB vmtoolsd 47721 3232 kB tuned 2684 4096 kB mysqld 37719 5736 kB munin-node 14059 18360 kB miniserv.pl 44877 26564 kB mysqld 35777 901208 kB mysqld 50045 SwapUsed Name Tgid Pid
- Pour déterminer l’occupation de l’espace de swap par l’ensemble des process démons mysqld (en s’appuyant sur
smaps
plutôt questatus
ci-dessus) :[mysql@mysqlserver ~]$ for pid in $(pidof mysqld) do awk 'BEGIN { swap_used=0 } /^Swap:/ { swap_used+=$2 } END { print "Occupation du swap par les process mysqld = " swap_used " kB" } ' /proc/$pid/smaps done Occupation du swap par les process mysqld = 185748 kB
- La commande
top
permet aussi de lister les process occupant l’espace de swap.
Danstop
, taper «O
» (o majuscule), puis «p
» pour trier sur la colonne swap qui sera ajoutée automatiquement dans votre affichage. Et n’oubliez pas de lire les premières lignes en haut de l’écran qui sont un petit récapitulatif de meminfo et vmstat 🙂
Pour une utilisation classique de la commande (sans ce tri sur la colonne swap), il est judicieux de faire varier la fréquence de rafraîchissement, d’autant qu’il est possible de descendre en dessous de la seconde : taper « s » puis « 0.5 » pour une demi-seconde. Une période de plusieurs secondes donnera un résultat différent dans le classement des process car l’analyse par le système est différente.
Je suis certain que maintenant vous n’utiliserez plustop
comme avant.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3. Vérifier la configuration des bases MySQL
Le but est d’ajuster les paramètres de configuration de chacune des bases en fonction de l’activité des bases, de leur rôle et des choix de répartition des ressources mémoire du serveur.
Certains paramètres ont un impact direct sur la consommation mémoire. Il est bon de les passer en revue et éventuellement de les réajuster. La plupart des paramètres peuvent être ajustés à chaud en version 5.7 de MySQL.
Pour les bases InnoDB, il est recommandé de ne pas utiliser le cache fichier de Linux. Pour cela, ajuster le paramètre innodb_flush_method=O_DIRECT
.
Ci-dessous vous trouverez la liste des paramètres qui ont le plus d’impact sur la consommation mémoire :
innodb_buffer_pool_size
Attention : 5 à 10% seront probablement alloués en sus. Surallocation nécessaire pour les structures des métadonnées, voire plus si vous utilisez la compression ou encore si la taille d’une page est inférieure à 16 Ko (cf.innodb_page_size
)
Par exemple 12 Go est souvent une valeur optimale pour 16 Go de RAM.innodb_additional_mem_pool_size
innodb_log_buffer_size
key_buffer_size
- buffer MyISAM similaire à
innodb_buffer_pool
query_cache_size
innodb_ft_total_cache_size
cache pour les index de recherche FULL TEXT, par défaut 610 Mo.
table_open_cache
table_definition_cache
open_files_limitperformance_schema
à passer à
off
max_connections
à limiter autant que faire se peut
tmp_table_size
Les deux sont liées aux allocations des tables temporaires qui ont aussi un impact non négligeable sur les ressources mémoire, et qui dépendent du nombre de connexions
max_heap_table_size
read_buffer_size
sort_buffer_size
dépendent du nombre de sessions !- Exemples de commandes pour lister la configuration actuelle :
mysql> SHOW GLOBAL STATUS LIKE 'innodb_buffer_pool%'; mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_buffer_pool_%'; mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_additional%'; mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_log_%'; mysql> SHOW GLOBAL VARIABLES LIKE '%_size'; mysql> SHOW GLOBAL VARIABLES LIKE 'max_connect%'; mysql> SHOW GLOBAL STATUS LIKE '%CONNEC%'; mysql> SHOW GLOBAL VARIABLES LIKE 'table%'; mysql> SELECT @@innodb_buffer_pool_size/1024/1024/1024; mysql> SELECT @@innodb_buffer_pool_instances; mysql> SELECT @@innodb_buffer_pool_chunk_size; mysql> SHOW GLOBAL VARIABLES LIKE "%table%cache%"; mysql> SHOW GLOBAL VARIABLES LIKE "%open_file%"; mysql> SHOW VARIABLES LIKE '%query_cache%'; mysql> SHOW GLOBAL VARIABLES LIKE 'innodb_ft_total_cache_size'; mysql> SHOW full processlist ; mysql> SHOW ENGINE PERFORMANCE_SCHEMA STATUS ; mysql> SHOW ENGINE INNODB STATUS ; mysql> SHOW GLOBAL VARIABLES LIKE "performance_schema%" ; mysql> SHOW GLOBAL STATUS LIKE "performance_schema%";
Si
performance_schema
est positionné à ON alors ce derniershow
surperformance_schema%
devrait ramener 0 sur toutes les lignes.
Pour éviter une dégradation des performances de votre moteur MySQL, il est conseillé d’avoir un nombre de fragments « chunk » qui n’excède pas 1000, c’est-à-dire le rapport entreinnodb_buffer_pool_size
etinnodb_buffer_pool_chunk_size
. - Pour évaluer l’allocation possible de la mémoire par un serveur MySQL à partir du nombre de sessions maximal :
SELECT ( @@key_buffer_size + @@query_cache_size + @@innodb_buffer_pool_size + @@innodb_additional_mem_pool_size + @@innodb_log_buffer_size + @@max_connections * ( @@read_buffer_size + @@read_rnd_buffer_size + @@sort_buffer_size + @@join_buffer_size + @@binlog_cache_size + @@thread_stack+ @@tmp_table_size ) ) / (1024 * 1024 ) AS MAX_MEMORY_Mo ;
- La taille du buffer cache du moteur MySQL peut être ajustée à chaud. Mais notez toutefois que cela ne sera effectif que lorsque toutes les transactions et opérations en cours provenant des API InnoDB seront terminées.
mysql> SET GLOBAL innodb_buffer_pool_size=402653184;
- Pour comptabiliser le nombre de tables InnoDB, et obtenir quelques statistiques sur l’utilisation de la mémoire :
[mysql@mysqlserver ~]$ mysql -e "SELECT concat(TABLE_SCHEMA, '.', TABLE_NAME) FROM information_schema.tables WHERE engine = 'InnoDB'" mysql> use performance_schema mysql> UPDATE setup_instruments SET ENABLED='YES' WHERE NAME LIKE 'memory/%'; mysql> SELECT * FROM memory_summary_global_by_event_name WHERE EVENT_NAME = 'memory/sql/TABLE'\G
Évidemment, il est beaucoup plus simple de procéder à ces réglages lorsque le serveur (la VM) n’héberge qu’une seule base. Si plusieurs bases sont mutualisées, tout est alors bien plus complexe. Dans ce cas, la première recommandation est d’éviter d’installer autre chose que des bases de production sur un serveur de production, et de faire preuve de bon sens 🙂
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4. Limiter l’activité de swapping
Vérification faite de la configuration de vos bases MySQL, vous souhaitez exploiter au plus juste la RAM disponible.
Vous avez noté par ailleurs que votre serveur subit très occasionnellement et de manière très brève un surcroît d’activité qui entraîne un petit surcoût d’allocation mémoire et approche la limite disponible. C’est d’ailleurs cette situation qui a déclenché l’activité de swapping : votre Linux est inquiet et commence à anticiper le manque de RAM car vous flirtez en permanence avec les 90% d’occupation de la RAM.
Vous en avez bien conscience et vous ne pouvez ou ne souhaitez pas pour l’instant ajouter de la RAM à votre VM/serveur (ce serait pourtant la meilleure solution si vous ne pouvez réduire votre configuration à l’étape précédente).
Votre souhait est maintenant de rassurer votre noyau et ainsi de limiter la pagination.
Le choix du déclenchement de cette pagination par le noyau peut être induit par le paramètre swappiness
(une valeur proche de 0
freine ce déclenchement).
- Par exemple pour modifier la sensibilité du noyau jusqu’au prochain reboot :
[root@mysqlserver ~]# cat /proc/sys/vm/swappiness 30 [root@mysqlserver ~]# sysctl vm.swappiness=10 [root@mysqlserver ~]# cat /proc/sys/vm/swappiness 10
Vous pouvez descendre jusqu’à 1 mais évitez 0.
- Pour prise en compte lors des prochains reboot, il vous faut modifier la valeur du paramètre
vm.swappiness
dans/etc/sysctl.conf
puis pour demander au système de relire le fichier, lancersysctl -p
- Pour libérer l’espace de swap (càd recharger les pages en mémoire) et vérifier le nouveau comportement du noyau (comme suite à un
reboot
), utilisez les commandesswapoff
etswapon
.
Ce sera utile par exemple dans le cas où le swap est plein à 100% depuis plusieurs jours :# swapoff -a && swapon -a
- La valeur 0 du paramètre noyau
panic_on_oom
indique que le système fera appel à sa fonction killer OOM lors d’un manque total de mémoire pour ainsi ne pas impacter la majeure partie des autres process.
Le paramètre noyauovercommit_memory
a aussi une incidence sur le comportement du noyau en cas de surcharge. Disons que si votre espace de swap est au moins équivalent à votre RAM, vous pourriez envisager de passer sa valeur à 2 sachant que la valeur par défaut est 0. Cette dernière correspond à un algorithme avec un bon compromis dans la plupart des cas.
Personnellement, je n’aime pas voir un serveur « swapper » plusieurs gigas de RAM. Les serveurs récents sont souvent dotés de dizaines de Go de RAM. Il me semble de bon sens de ne pas systématiquement affecter autant de swap que de RAM malgré ce que recommande certains éditeurs. Pour preuve les changements des pré-requis d’Oracle tout au long des releases de sa version 11.
Le paramètre noyauovercommit_ratio
n’est fonctionnel que lorsque la valeur 2 est affectée au paramètreovercommit_memory
. Il correspond au pourcentage de RAM à prendre en compte.# cat /proc/sys/vm/overcommit_memory # cat /proc/sys/vm/panic_on_oom 0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5. Protéger au mieux les bases MySQL de production
Le but est de privilégier la disponibilité des bases de production sur des serveurs qui hébergent aussi des bases de recettes, de développement ou de formation.
Comme pour les bases Oracle, les bases MySQL savent exploiter les HugePages aussi bien pour le cache principal que pour les zones de mémoire supplémentaires.
Plusieurs articles de ce blog traitent déjà de ce sujet en détail. En résumé, il est tout d’abord nécessaire de procéder à la configuration du système comme par exemple la déclaration du nombre de pages à allouer par le noyau linux en tant que HugePages, puis à paramétrer MySQL en conséquent :
- Pour la configuration du système, les paramètres devront être ajoutés dans le fichier
/etc/sysctl.conf
Les exemples ci-dessous sont tous basés sur un objectif d’affectation de 1 Go de HugePages.# echo "vm.nr_hugepages=512" >> /etc/sysctl.conf # 512 pages de 2 Mo, soit 1 Go # echo "vm.hugetlb_shm_group=500" >> /etc/sysctl.conf # remplacer 500 par votre n° de groupe mysql, càd le résultat de id -g mysql
Ajustez éventuellement la configuration de la mémoire partagée grâce aux paramètres
kernel.shmmax
(valeur en octets) etkernel.shmall
(valeur en nombre de pages).
Pour demander au système de relire le fichier, utilisez la commandesysctl
.
Les modifications peuvent être ensuite visualisées dans/proc/meminfo
.sysctl -p cat /proc/meminfo
- Vérifiez et ajustez aussi les limites systèmes dans le fichier
/etc/security/limits.conf
» (ou/etc/security/limits.d/99-mysql-limits.conf
en RHEL 7), puis vérifiez aveculimit -l
Par exemple :mysql soft memlock 1048576 mysql hard memlock 1048576 # soit 512 * 2048
- Si des Transparent HugePages ont été allouées (cf.
grep AnonHugePages /proc/meminfo
), il est préférable de les désactiver soit en ajoutant le paramètretransparent_hugepage=never
dans le fichiergrub.conf
à la fin de la ligne du lancement du noyau (avant le chargement du ramdisqueinitrd
), soit par un script d’init contenant les commandes suivantes (en RHEL 7 n’oubliez pas de déclarer le script dans SElinux) :echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag
- Pour activer l’emploi des HugePages disponibles à MySQL, lancer le serveur avec l’option
--large-pages,
ou ajouter les lignes suivantes dans votre fichier de configurationmy.cnf
:[mysqld] large-pages
Si le serveur InnoDB ne peut allouer sa mémoire dans les HugePages, il se contentera des pages mémoire traditionnelles et tracera un warning dans le log d’erreurs du type « Warning: Using conventional memory pool ».
En sus de la configuration des HugePages, il est possible d’indiquer au noyau que tel process est plus important à vos yeux que les autres.
- Pour qu’un process démon
mysqld
soit moins candidat au kill du Killer OOM il suffira d’ajuster la valeur duoom_score_adj
qui lui est propre a une valeur basse.
Pour lister la valeur actuelle du paramètre (0 par défaut) pour chacune des bases MySQL :for pid in $(pidof mysqld) do echo -e "\n oom_score_adj de mysqld pid $pid = \c" cat /proc/"$pid"/oom_score_adj done
Les valeurs acceptées vont de -1000 à +1000. Une valeur négative va réduire les chances que le process soit élu par le Killer OOM le cas échéant. Toutefois considérez que ce n’est qu’une indication et que le noyau pourra tout de même en juger autrement.
Exemple d’affectation de la valeur négative -800 à votre process préféré, et constat du résultat du score à une potentielle élection :cat /proc/"$pid_mysqld_prod"/oom_score 24 echo '-800' >/proc/"$pid_mysqld_prod"/oom_score_adj cat /proc/"$pid_mysqld_prod"/oom_score 2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6. Point particulier : les architectures NUMA
Dernier point à prendre en compte : les architectures NUMA peuvent aussi être à l’origine du déclenchement d’une activité de swapping en cas de manque de mémoire d’un des nœuds NUMA.
La commande numactl
permet de retrouver le nombre de nœuds et leur numéro (0, 1 …), la mémoire affectée à chaque nœud, ainsi que la « distance » entre les nœuds (càd le coût d’accès à chaque zone de mémoire).
Par exemple, la fin du résultat de la commande suivante, à la suite de « node distances
« , fait apparaître un petit tableau qui présente le coût d’accès d’une zone de mémoire pour chacun des nœuds. Dans cet exemple, il est possible d’en déduire que pour chacun des nœuds, l’accès à la mémoire dédiée à l’autre nœud présente un surcoût du simple au double.
$ numactl --hardware available: 2 nodes (0-1) node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 node 0 size: 262010 MB node 0 free: 68707 MB node 1 cpus: 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 node 1 size: 262144 MB node 1 free: 2666 MB node distances: node 0 1 0: 10 21 1: 21 10
La consultation alors de /proc/$(pidof mysqld)/numa_maps
permet de lister l’allocation mémoire d’un process et de constater de sa répartition entre chaque zone mémoire.
Sur un serveur hébergeant un grand nombre de bases Oracle, le grep suivant a permis de faire ressortir uniquement les process pour lesquels les valeurs sont problématiques :
for process in /proc/[0-9][0-9]* do if egrep "N0=[0-9][0-9][0-9][0-9][0-9][0-9]* .*N1=[0-9][0-9][0-9][0-9][0-9][0-9]*" $process/numa_maps 2>/dev/null then echo -e "$process $(cat $process/cmdline) \n" fi done
Par exemple, notez les valeurs retournées pour N0 et N1 ci-dessous :
e9600000 default anon=68096 dirty=68096 active=68095 N0=31604 N1=36492 /proc/140963 /u01/app/oracle/product/agent12c/core/12.1.0.5.0/jdk/bin/java-Xmx303M-XX:MaxPermSize=96M-server-Djava.security.egd=file:///dev/./urandom-Dsun.lang.ClassLoader.allowArraySyntax=true-XX: +UseLinuxPosixThreadCPUClocks-XX:...
Pour s’affranchir de cette problématique de mémoire dédiée à chaque nœud, il suffit d’ajouter le paramètre suivant à votre fichier de configuration MySQL :
innodb_numa_interleave=1 # version 5.6 minimum
Si votre version de MySQL est antérieure à la 5.6, il vous faudra plutôt lancer votre serveur avec la commande suivante :
# numactl --interleave=all
Ou encore prévoir une section mysqld_safe dans votre my.cnf qui fera mention par exemple d’un script mysqld_numa comme ci-dessous :
[mysqld_safe] # wrapper mysqld mysqld=mysqld_numa
Script mysqld_numa.sh :
#!/bin/sh numactl=/usr/bin/numactl mysqld=/usr/sbin/mysqld # MAJ le chemin d’accès du démon exec "$numactl" --interleave=all "$mysqld" "$@"
Dernière recommandation commune à bien d’autres SGBD sous Linux, ajouter la déclaration suivante dans /etc/sysctl.conf
vm.zone_reclaim_mode = 0