L'un des facteurs clés d'un serveur de base de données MySQL performant est d'avoir une bonne allocation et utilisation de la mémoire, en particulier lors de son exécution dans un environnement de production. Mais comment déterminer si l'utilisation de MySQL est optimisée ? Est-il raisonnable d'avoir une utilisation élevée de la mémoire ou cela nécessite-t-il un réglage fin ? Que se passe-t-il si je rencontre une fuite de mémoire ?
Abordons ces sujets et montrons ce que vous pouvez vérifier dans MySQL pour déterminer les traces d'utilisation élevée de la mémoire.
Allocation de mémoire dans MySQL
Avant de plonger dans le titre du sujet spécifique, je vais juste donner une brève information sur la façon dont MySQL utilise la mémoire. La mémoire joue un rôle important dans la vitesse et l'efficacité lors de la gestion de transactions simultanées et de l'exécution de requêtes volumineuses. Chaque thread de MySQL demande de la mémoire qui est utilisée pour gérer les connexions client, et ces threads partagent la même mémoire de base. Des variables telles que thread_stack (pile pour les threads), net_buffer_length (pour le tampon de connexion et le tampon de résultat), ou avec max_allowed_packet où la connexion et le résultat s'agrandissent dynamiquement jusqu'à cette valeur si nécessaire, sont des variables qui affectent l'utilisation de la mémoire. Lorsqu'un thread n'est plus nécessaire, la mémoire qui lui est allouée est libérée et renvoyée au système à moins que le thread ne retourne dans le cache des threads. Dans ce cas, la mémoire reste allouée. Les jointures de requête, les caches de requête, le tri, le cache de table, les définitions de table nécessitent de la mémoire dans MySQL, mais celles-ci sont attribuées avec des variables système que vous pouvez configurer et définir.
Dans la plupart des cas, les variables spécifiques à la mémoire définies pour une configuration sont ciblées sur une configuration spécifique basée sur le stockage, telle que MyISAM ou InnoDB. Lorsqu'une instance mysqld apparaît dans le système hôte, MySQL alloue des tampons et des caches pour améliorer les performances des opérations de base de données en fonction des valeurs définies dans une configuration spécifique. Par exemple, les variables les plus courantes que chaque DBA définira dans InnoDB sont les variables innodb_buffer_pool_size et innodb_buffer_pool_instances qui sont toutes deux liées à l'allocation de mémoire du pool de tampons qui contient les données mises en cache pour les tables InnoDB. C'est souhaitable si vous disposez d'une mémoire importante et que vous prévoyez de gérer de grosses transactions en définissant innodb_buffer_pool_instances pour améliorer la concurrence en divisant le pool de mémoire tampon en plusieurs instances de pool de mémoire tampon.
Alors que pour MyISAM, vous devez gérer key_buffer_size pour gérer la quantité de mémoire que le tampon de clé gérera. MyISAM alloue également un tampon pour chaque thread concurrent qui contient une structure de table, des structures de colonne pour chaque colonne et un tampon de taille 3 * N sont alloués (où N est la longueur de ligne maximale, sans compter les colonnes BLOB). MyISAM maintient également un tampon de ligne supplémentaire pour un usage interne.
MySQL alloue également de la mémoire pour les tables temporaires à moins qu'elle ne devienne trop grande (déterminée par tmp_table_size et max_heap_table_size). Si vous utilisez des tables MEMORY et que la variable max_heap_table_size est définie très haut, cela peut également prendre beaucoup de mémoire car la variable système max_heap_table_size détermine la taille maximale d'une table et il n'y a pas de conversion au format sur disque.
MySQL dispose également d'un schéma de performances qui est une fonctionnalité permettant de surveiller les activités de MySQL à un niveau inférieur. Une fois activé, il alloue dynamiquement la mémoire de manière incrémentielle, en adaptant son utilisation de la mémoire à la charge réelle du serveur, au lieu d'allouer la mémoire requise lors du démarrage du serveur. Une fois la mémoire allouée, elle n'est pas libérée tant que le serveur n'est pas redémarré.
MySQL peut également être configuré pour allouer de grandes zones de mémoire pour son pool de mémoire tampon si vous utilisez Linux et si le noyau est activé pour le support de grandes pages, c'est-à-dire en utilisant HugePages.
Ce qu'il faut vérifier une fois que la mémoire MySQL est élevée
Vérifier les requêtes en cours
Il est très courant que les DBA MySQL s'intéressent d'abord à ce qui se passe avec le serveur MySQL en cours d'exécution. Les procédures les plus élémentaires sont la vérification de la liste des processus, la vérification de l'état du serveur et la vérification de l'état du moteur de stockage. Pour faire ces choses, en gros, il vous suffit d'exécuter la série de requêtes en vous connectant à MySQL. Voir ci-dessous :
Pour afficher les requêtes en cours d'exécution,
mysql> SHOW [FULL] PROCESSLIST;
L'affichage de la liste de processus actuelle révèle les requêtes qui s'exécutent activement ou même les processus inactifs ou endormis. Il est très important et constitue une routine importante d'avoir un enregistrement des requêtes en cours d'exécution. Comme indiqué sur la façon dont MySQL alloue la mémoire, l'exécution de requêtes utilisera l'allocation de mémoire et peut entraîner des problèmes de performances considérables si elle n'est pas surveillée.
Afficher les variables d'état du serveur MySQL,
mysql> SHOW SERVER STATUS\G
ou filtrer des variables spécifiques comme
mysql> SHOW SERVER STATUS WHERE variable_name IN ('<var1>', 'var2'...);
Les variables d'état de MySQL vous servent d'informations statistiques pour saisir des données métriques afin de déterminer les performances de votre MySQL en observant les compteurs donnés par les valeurs d'état. Il y a certaines valeurs ici qui vous donnent un aperçu qui a un impact sur l'utilisation de la mémoire. Par exemple, vérifier le nombre de threads, le nombre de caches de table ou l'utilisation du pool de mémoire tampon,
...
| Created_tmp_disk_tables | 24240 |
| Created_tmp_tables | 334999 |
…
| Innodb_buffer_pool_pages_data | 754 |
| Innodb_buffer_pool_bytes_data | 12353536 |
...
| Innodb_buffer_pool_pages_dirty | 6 |
| Innodb_buffer_pool_bytes_dirty | 98304 |
| Innodb_buffer_pool_pages_flushed | 30383 |
| Innodb_buffer_pool_pages_free | 130289 |
…
| Open_table_definitions | 540 |
| Open_tables | 1024 |
| Opened_table_definitions | 540 |
| Opened_tables | 700887 |
...
| Threads_connected | 5 |
...
| Threads_cached | 2 |
| Threads_connected | 5 |
| Threads_created | 7 |
| Threads_running | 1 |
Afficher l'état du moniteur du moteur, par exemple, l'état InnoDB
mysql> SHOW ENGINE INNODB STATUS\G
Le statut InnoDB révèle également le statut actuel des transactions traitées par le moteur de stockage. Il vous donne la taille du tas d'une transaction, des index de hachage adaptatifs révélant son utilisation du tampon, ou vous montre les informations du pool de tampons innodb comme dans l'exemple ci-dessous :
---TRANSACTION 10798819, ACTIVE 0 sec inserting, thread declared inside InnoDB 1201
mysql tables in use 1, locked 1
1 lock struct(s), heap size 1136, 0 row lock(s), undo log entries 8801
MySQL thread id 68481, OS thread handle 139953970235136, query id 681821 localhost root copy to tmp table
ALTER TABLE NewAddressCode2_2 ENGINE=INNODB
…
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 528, free list len 43894, seg size 44423, 1773 merges
merged operations:
insert 63140, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 553193, node heap has 1 buffer(s)
Hash table size 553193, node heap has 637 buffer(s)
Hash table size 553193, node heap has 772 buffer(s)
Hash table size 553193, node heap has 1239 buffer(s)
Hash table size 553193, node heap has 2 buffer(s)
Hash table size 553193, node heap has 0 buffer(s)
Hash table size 553193, node heap has 1 buffer(s)
Hash table size 553193, node heap has 1 buffer(s)
115320.41 hash searches/s, 10292.51 non-hash searches/s
...
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 2235564032
Dictionary memory allocated 3227698
Internal hash tables (constant factor + variable factor)
Adaptive hash index 78904768 (35404352 + 43500416)
Page hash 277384 (buffer pool 0 only)
Dictionary cache 12078786 (8851088 + 3227698)
File system 1091824 (812272 + 279552)
Lock system 5322504 (5313416 + 9088)
Recovery system 0 (0 + 0)
Buffer pool size 131056
Buffer pool size, bytes 2147221504
Free buffers 8303
Database pages 120100
Old database pages 44172
Modified db pages 108784
Pending reads 0
Pending writes: LRU 2, flush list 342, single page 0
Pages made young 533709, not young 181962
3823.06 youngs/s, 1706.01 non-youngs/s
Pages read 4104, created 236572, written 441223
38.09 reads/s, 339.46 creates/s, 1805.87 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 12 / 1000 not 5 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 120100, unzip_LRU len: 0
I/O sum[754560]:cur[8096], unzip sum[0]:cur[0]
…
Une autre chose à ajouter, vous pouvez également utiliser le schéma de performance et le schéma sys pour surveiller la consommation et l'utilisation de la mémoire par votre serveur MySQL. Par défaut, la plupart des instrumentations sont désactivées par défaut, il y a donc des choses manuelles à faire pour l'utiliser.
Vérifier la permutation
De toute façon, il est probable que MySQL échange sa mémoire sur le disque. Il s'agit souvent d'une situation très courante, en particulier lorsque le serveur MySQL et le matériel sous-jacent ne sont pas configurés de manière optimale parallèlement aux exigences attendues. Dans certains cas, la demande de trafic n'a pas été anticipée, la mémoire peut augmenter de plus en plus, en particulier si de mauvaises requêtes sont exécutées, ce qui consomme ou utilise beaucoup d'espace mémoire, entraînant une dégradation des performances, car les données sont sélectionnées sur le disque plutôt que sur le tampon. Pour vérifier l'échange, exécutez simplement la commande freemem ou vmstat comme ci-dessous,
[[email protected] ~]# free -m
total used free shared buff/cache available
Mem: 3790 2754 121 202 915 584
Swap: 1535 39 1496
[[email protected] ~]# vmstat 5 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 40232 124100 0 937072 2 3 194 1029 477 313 7 2 91 1 0
0 0 40232 123912 0 937228 0 0 0 49 1247 704 13 3 84 0 0
1 0 40232 124184 0 937212 0 0 0 35 751 478 6 1 93 0 0
0 0 40232 123688 0 937228 0 0 0 15 736 487 5 1 94 0 0
0 0 40232 123912 0 937220 0 0 3 74 1065 729 8 2 89 0 0
Vous pouvez également vérifier en utilisant procfs et collecter des informations telles que /proc/vmstat ou /proc/meminfo.
Utiliser Perf, gdb et Valgrind avec Massif
L'utilisation d'outils tels que perf, gdb et valgrind vous aide à explorer une méthode plus avancée pour déterminer l'utilisation de la mémoire MySQL. Il y a des moments où un résultat intéressant devient un mystère de résolution de la consommation de mémoire qui conduit à votre perplexité dans MySQL. Cela rend nécessaire d'avoir plus de scepticisme et l'utilisation de ces outils vous aide à étudier comment MySQL utilise la mémoire de gestion de son allocation à son utilisation pour le traitement des transactions ou des processus. Ceci est utile, par exemple, si vous observez que MySQL se comporte de manière anormale, ce qui peut entraîner une mauvaise configuration ou entraîner des fuites de mémoire.
Par exemple, l'utilisation de perf dans MySQL révèle plus d'informations dans un rapport au niveau du système :
[[email protected] ~]# perf report --input perf.data --stdio
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 54K of event 'cpu-clock'
# Event count (approx.): 13702000000
#
# Overhead Command Shared Object Symbol
# ........ ....... ................... ...................................................................................................................................................................................................
#
60.66% mysqld [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
2.79% mysqld libc-2.17.so [.] __memcpy_ssse3
2.54% mysqld mysqld [.] ha_key_cmp
1.89% mysqld [vdso] [.] __vdso_clock_gettime
1.05% mysqld mysqld [.] rec_get_offsets_func
1.03% mysqld mysqld [.] row_sel_field_store_in_mysql_format_func
0.92% mysqld mysqld [.] _mi_rec_pack
0.91% mysqld [kernel.kallsyms] [k] finish_task_switch
0.90% mysqld mysqld [.] row_search_mvcc
0.86% mysqld mysqld [.] decimal2bin
0.83% mysqld mysqld [.] _mi_rec_check
….
Puisque cela peut être un sujet spécial à approfondir, nous vous suggérons de consulter ces très bons blogs externes comme références, perf Basics for MySQL Profiling, Finding MySQL Scaling Problems Using perf, ou apprendre à déboguer en utilisant valgrind avec massif.
Un moyen efficace de vérifier l'utilisation de la mémoire MySQL
L'utilisation de ClusterControl soulage les routines fastidieuses telles que parcourir vos runbooks ou même créer vos propres playbooks qui fourniraient des rapports pour vous. Dans ClusterControl, vous avez des tableaux de bord (utilisant SCUMM) où vous pouvez avoir un aperçu rapide de votre ou vos nœuds MySQL. Par exemple, en consultant le tableau de bord général de MySQL,
vous pouvez déterminer les performances du nœud MySQL,
Vous voyez que les images ci-dessus révèlent des variables qui ont un impact sur l'utilisation de la mémoire MySQL. Vous pouvez vérifier comment les métriques pour trier les caches, les tables temporaires, les threads connectés, le cache de requête ou les moteurs de stockage innodb buffer pool ou le key buffer de MyISAM.
L'utilisation de ClusterControl vous offre un outil utilitaire unique où vous pouvez également vérifier les requêtes en cours d'exécution pour déterminer les processus (requêtes) qui peuvent avoir un impact sur l'utilisation élevée de la mémoire. Voir ci-dessous pour un exemple,
Voir les variables d'état de MySQL est assez simple,
Vous pouvez même aller dans Performances -> Statut Innodb pour révéler le statut InnoDB actuel de vos nœuds de base de données. De plus, dans ClusterControl, un incident est détecté, il essaiera de collecter l'incident et affichera l'historique sous forme de rapport qui vous fournira le statut InnoDB, comme indiqué dans notre précédent blog sur MySQL Freeze Frame.
Résumé
Dépanner et diagnostiquer votre base de données MySQL lorsque vous suspectez une utilisation élevée de la mémoire n'est pas si difficile tant que vous connaissez les procédures et les outils à utiliser. L'utilisation du bon outil vous offre plus de flexibilité et une productivité plus rapide pour fournir des correctifs ou des solutions avec une chance d'obtenir de meilleurs résultats.