web-dev-qa-db-fra.com

Perte de performances extrême due au passage de Mysql 5.7.23 à Mysql 8.0 (état, configuration inclus)

Je suis resté assis dessus pendant 12 heures d'affilée (il est midi maintenant, donc je vais lire/répondre quand je me suis réveillé).
J'ai fait la grande faute de recommander une mise à niveau de la base de données de notre environnement productif pour augmenter les performances.
Nous ne pouvons PAS revenir en arrière, c'est près de 6 terrabytes de stockage et les rétrogradations ne sont pas possibles avec mysql 8.0.
L'utilisation d'une sauvegarde d'instantané préalable à la mise à niveau n'est pas une solution (jours de travail).

Je voudrais résoudre les performances horribles résultant du passage du serveur à la dernière version

Quelques détails:
Environnement : Linux Stretch sur AWS i3.8xlarge (32 processeurs, 240 Go de RAM)
Serveur : Mysql 8.0 dans le conteneur mysql/Docker utilisant des montures de liaison et la mise en réseau de l'hôte
Stockage : stockage Amazon AWS EBS IO1 (6 To) avec 20 000 IOPS réservés.
Le stockage fournit 500 Mo/s à 600 Mo/s dans les tests fio.
CPU: Habituellement, les 32 cœurs sont utilisés à 50-60% mais depuis mysql 8.0 il est inactif (10 à 15% utilisé)
[~ # ~] ram [~ # ~] : 200 Go sont dédiés à mysql, avant 8.0 cela prenait peu de temps et c'était utilisé. Maintenant, cela prend plusieurs heures avant que mysql soit capable de remplir le tampon.

Le problème principal: La vitesse a chuté environ 20 fois par rapport à 5,7
Innodb/mysql n'utilise pas le disque efficacement.
En utilisant la configuration d'origine, il lisait à 15 Mo/s (les comptages simples prenaient quelques minutes car le fichier IBD n'était pas lu correctement)
J'ai depuis désactivé le schéma de performances, ce qui a permis au moins de faire à nouveau fonctionner le serveur à 10% de charge.
.
J'ai essayé d'augmenter le nombre de threads de lecture/écriture à 64 (ce qui a complètement bloqué le serveur)

J'ai investi 8 bonnes heures pour essayer de régler la configuration mysql, j'ai réussi à "pousser" mysql en utilisant seulement 5 Mo/s jusqu'à maintenant jusqu'à 50 Mo/s.

Pour vous assurer:
Bien sûr, j'ai testé le disque IO de l'intérieur du docker, c'est exactement comme à l'extérieur.
Le disque principal est principalement inactif, les autres disques sont complètement inactifs.
Le système est inactif à 90% lorsqu'il doit être occupé.

Statut Innodb:
https://Pastebin.com/XDgyNbk

Configuration Mysql:

[mysqld]
user=mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
datadir         = /var/lib/mysql

tmpdir          = /mysql_tmp  

# compatibility
default_authentication_plugin=mysql_native_password
character_set_server=latin1
collation_server=latin1_swedish_ci



log-error       = /var/log/mysql_error.log
bind-address    = 0.0.0.0
sql_mode= "NO_ENGINE_SUBSTITUTION"

interactive_timeout     = 3600
wait_timeout            = 900

max_allowed_packet      = 64M
thread_stack            = 256K
thread_cache_size       = 192

max_connections         = 1600
max_user_connections    = 1500

#query_cache_limit       = 3M
#query_cache_size        = 200M
#query_cache_type       = 1
table_open_cache        = 2500


key_buffer_size          = 64M   # index in memory for myisam
innodb_buffer_pool_size = 190G
innodb_log_file_size = 256M
tmp_table_size = 250M
max_heap_table_size = 250M
join_buffer_size = 2M
#pagecleaners  - those were uncommented on 5.7
#innodb_buffer_pool_instances=8
#innodb_page_cleaners=2
innodb_io_capacity=5000
innodb_io_capacity_max=20000

# tried 64, that totally stalled the database
innodb_read_io_threads = 8
innodb_write_io_threads = 8

#in pre 5.7 times I had consistent 300mb/sec writes, now it's useless
innodb_lru_scan_depth=256

skip-name-resolve

secure_file_priv=""
#innodb_checksum_algorithm = crc32
#binlog_checksum = CRC32

# this one at least made it possible so I can go to bed, with performance_schema the database was unuseable    
performance_schema=OFF
skip-log-bin 

Sysctl fs :

fs.aio-max-nr = 1048576
fs.aio-nr = 139264
fs.binfmt_misc.status = enabled
fs.dentry-state = 355223        335269  45      0       0       0
fs.dir-notify-enable = 1
fs.epoll.max_user_watches = 51660308
fs.file-max = 25224638
fs.file-nr = 19136      0       25224638
fs.inode-nr = 70145     5686
fs.inode-state = 70145  5686    0       0       0       0       0
fs.inotify.max_queued_events = 16384
fs.inotify.max_user_instances = 128
fs.inotify.max_user_watches = 8192
fs.lease-break-time = 45
fs.leases-enable = 1
fs.mqueue.msg_default = 10
fs.mqueue.msg_max = 10
fs.mqueue.msgsize_default = 8192
fs.mqueue.msgsize_max = 8192
fs.mqueue.queues_max = 256
fs.nr_open = 1048576
fs.overflowgid = 65534
fs.overflowuid = 65534
fs.pipe-max-size = 1048576
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.quota.allocated_dquots = 0
fs.quota.cache_hits = 0
fs.quota.drops = 0
fs.quota.free_dquots = 0
fs.quota.lookups = 0
fs.quota.reads = 0
fs.quota.syncs = 62
fs.quota.warnings = 1
fs.quota.writes = 0
fs.suid_dumpable = 0

Instantané iostat:

avg-cpu:  %user   %Nice %system %iowait  %steal   %idle
          13.86    0.71   10.84    3.23    0.11   71.26

Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
xvdh           1795.31     13923.89     26159.95  256609017  482112504

Comme vous pouvez le voir, il suffit de lire à 14 Mo/sec, d'écrire à 26 Mo/sec.
Avec mysql 5.7, il faisait jusqu'à 200 Mo de lecture et d'écriture.

Le disque est essentiellement inactif. Il peut fournir 10 fois les performances mais pour une raison inconnue, innodb/mysql ne le font plus.

Mise à jour: Variables et état global: https://Pastebin.com/pzhXV7hq
https://Pastebin.com/jBTYLbY6

Un autre changement que j'ai été obligé de faire:
J'ai une centaine d'utilisateurs se connectant par seconde via Apache/php, chaque connexion déclenchait généralement un "SELECT count (*) FROM information_schema.processlist" de ces sélections, j'ai donc fait une tâche asynchrone qui insère la liste de processus dans une table innodb toutes les 5 secondes.
C'est juste un autre signe à quel point le nouveau mysql est moins performant, il est même étouffé par les listes de processus.

ulimit

   core file size          (blocks, -c) 0
    data seg size           (kbytes, -d) unlimited
    scheduling priority             (-e) 0
    file size               (blocks, -f) unlimited
    pending signals                 (-i) 985342
    max locked memory       (kbytes, -l) 64
    max memory size         (kbytes, -m) unlimited
    open files                      (-n) 65536
    pipe size            (512 bytes, -p) 8
    POSIX message queues     (bytes, -q) 819200
    real-time priority              (-r) 0
    stack size              (kbytes, -s) 8192
    cpu time               (seconds, -t) unlimited
    max user processes              (-u) 985342
    virtual memory          (kbytes, -v) unlimited
    file locks                      (-x) unlimited

Grande mise à jour
J'ai passé une demi-journée à lire des internas, à prendre en considération vos suggestions et ce que l'on m'a dit sur IRC.
J'ai fait l'opposé du professionnel: j'ai fait 10 changements à la fois, je ne peux pas me permettre autant de redémarrages dans un processus de changement graduel:
1) Donner au système de nombreuses possibilités d'écriture parallèle sans l'étouffer

innodb_read_io_threads = 16  
innodb_write_io_threads = 16  
innodb_thread_concurrency=64  # cpus*2  

2) Accélération de la synchronisation en arrière-plan:

 innodb_lru_scan_depth=100  

3) Désactiver les paramètres de fiabilité les plus élevés qui ont un impact dur sur les performances

performance_schema=OFF
skip-log-bin 
sync_binlog=0 
innodb_flush_log_at_trx_commit=0  # not crash safe, 0 is crash safe  

4) Plus de multithreading dans la mémoire du backend

innodb_buffer_pool_instances=12  

5) Augmentation significative des fichiers journaux, augmentation modérée de la mémoire tampon des fichiers journaux

innodb_log_file_size = 3G # 
innodb_log_buffer_size = 64M

Que s'est-il passé: Augmentation d'environ 10 fois les performances de lecture, 1,5 fois les performances d'écriture, je ne suis pas là où je veux être mais c'est 15 fois plus rapide qu'auparavant !
L'utilisation des IOPS a doublé de ~ 5-6k à 9k-12k, donc je suis à 60% IO usage
L'utilisation du processeur est passée de 7% à 50%

Mon objectif serait de 80% IO et l'utilisation du CPU par la base de données, je pense que d'autres variables goulot d'étranglement.

En utilisation temps réel: j'ai un énorme insert en cours d'exécution avant et après le changement (à côté de la charge habituelle).
Avant le changement, la vitesse était d'environ 3000 lignes par seconde, après les changements ci-dessus, elle est de 8000 lignes par seconde.

Je pensais partager cela car le changement de performance est extrême et je n'ai atteint que 50% de ce qui devrait être possible.

Mise à jour
Je pense que le problème peut être considéré comme à moitié résolu, j'ai fait une autre mise à jour après la précédente avec succès et les performances sont maintenant acceptables.

Les dernières modifications concernaient les threads d'écriture/lecture. Je les ai mis à 32 chacun.
tampon d'écriture augmenté à 128M (pour ma lourde charge de travail plus élevée pourrait être meilleure)
fichiers journaux augmentés à 8 Go
buffer_pool_instances augmenté à 64 (max) pour une meilleure fragmentation de la mémoire
page_cleaners est passé à 64 (max) pour en avoir un pour chaque instance de tampon.

les performances d'écriture ont augmenté de ~ 20%, les performances de lecture ont augmenté de ~ 30%.

Cela a pris 24 heures pour que mysql fonctionne correctement, ce n'est certainement pas une simple mise à niveau.

Dernier état:

Current configuration: https://Pastebin.com/9vsbEQxt
show engine status innodb: https://Pastebin.com/kCjnmtze
show global variables: https://Pastebin.com/aMdQxWcA
global status: https://Pastebin.com/VbG1yzHX
4
John

Examen de GLOBAL STATUS et VARIABLES

Observations:

  • Version: 8.0.13
  • 240 Go de RAM
  • Uptime = 10:47:09; certaines valeurs GLOBAL STATUS peuvent ne pas encore être significatives.
  • Vous n'êtes pas en cours d'exécution sur Windows.
  • Exécution de la version 64 bits
  • Vous semblez exécuter entièrement (ou principalement) InnoDB.

Les problèmes les plus importants:

Même si table_open_cache est élevé, il peut être bon de le relever davantage. Augmenter table_open_cache_instances à 32 peut aussi aider.

innodb_buffer_pool_instances = 16; idem pour innodb_page_cleaners.

Augmenter innodb_log_file_size à 8G; voir le manuel 8.0 pour plus de détails sur la façon d'y parvenir (les choses ont changé).

Pourquoi as-tu innodb_flush_method = fsync? O_DIRECT est recommandé dans de nombreuses situations.

Rétrécir long_quer_time; vérifier le slowlog; il y a quelques requêtes coquines sur lesquelles nous devons enquêter.

CREATE TABLE toutes les 5 secondes? Que se passe-t-il? Il est inhabituel d'en faire plus que quelques-uns par jour .

REPLACE est principalement supplanté par IODKU; quel est votre cas d'utilisation?

Vous utilisez beaucoup diverses commandes SHOW. Leur implémentation a radicalement changé en 8.0 et je m'attends à ce qu'ils soient plus rapides en 8.0, mais cela pourrait être une source de ralentissement.

dix TRUNCATE TABLE par minute? Quoi de neuf? Si vous remplacez le contenu d'une table, il est généralement préférable de créer une nouvelle table, remplissez-la, puis utilisez RENAME TABLE pour l'échanger atomiquement en place.

Détails et autres observations:

( Innodb_buffer_pool_pages_flushed ) = 96,254,291 / 38829 = 2478 /sec - Écrit (rince)

( Opened_tables ) = 104,524 / 38829 = 2.7 /sec - Fréquence d'ouverture des tables - augmenter table_open_cache

( table_open_cache ) = 19,000 - Nombre de descripteurs de table à mettre en cache - Plusieurs centaines est généralement bon.

( Table_open_cache_misses ) = 104,520 / 38829 = 2.7 /sec - Peut-être besoin d'augmenter table_open_cache

( innodb_buffer_pool_size / innodb_buffer_pool_instances ) = 181248M / 8 = 22656MB - Taille de chaque instance de buffer_pool. - Une instance doit faire au moins 1 Go. Dans une très grande RAM, 16 instances.

( innodb_page_cleaners / innodb_buffer_pool_instances ) = 4 / 8 = 0.5 - page_cleaners - Recommande de définir innodb_page_cleaners sur innodb_buffer_pool_instances

( (Innodb_buffer_pool_reads + Innodb_buffer_pool_pages_flushed) ) = ((23804167 + 96254291) ) / 38829 = 3091 /sec - E/S InnoDB

( Innodb_os_log_written ) = 249,997,739,520 / 38829 = 6438428 /sec - Ceci est un indicateur de l'occupation d'InnoDB.

( Innodb_log_writes ) = 411,565,494 / 38829 = 10599 /sec

( Innodb_os_log_written / (Uptime / 3600) / innodb_log_files_in_group / innodb_log_file_size ) = 249,997,739,520 / (38829 / 3600) / 2 / 256M = 43.2 - Ratio - (voir minutes)

( Uptime / 60 * innodb_log_file_size / Innodb_os_log_written ) = 38,829 / 60 * 256M / 249997739520 = 0.695 - Minutes entre les rotations du journal InnoDB À partir de 5.6.8, cela peut être modifié dynamiquement; assurez-vous également de modifier my.cnf. - (La recommandation de 60 minutes entre les rotations est quelque peu arbitraire.) Ajustez innodb_log_file_size. (Ne peut pas changer dans AWS.)

( innodb_flush_method ) = innodb_flush_method = fsync - Comment InnoDB devrait demander au système d'exploitation d'écrire des blocs. Suggérez O_DIRECT ou O_ALL_DIRECT (Percona) pour éviter la double mise en mémoire tampon. (Au moins pour Unix.) Voir chrischandler pour une mise en garde concernant O_ALL_DIRECT

( Innodb_row_lock_waits ) = 41,325 / 38829 = 1.1 /sec - La fréquence à laquelle un verrouillage de ligne est retardé. - Peut être causé par des requêtes complexes qui pourraient être optimisées.

( Innodb_dblwr_writes ) = 1,177,834 / 38829 = 30 /sec - "Doublewrite buffer" écrit sur le disque. Les "doubles écritures" sont une fonctionnalité de fiabilité. Certaines versions/configurations plus récentes n'en ont pas besoin. - (Symptôme d'autres problèmes)

( Innodb_row_lock_current_waits ) = 54 - Le nombre de verrous de ligne actuellement attendus par les opérations sur les tables InnoDB. Zéro est assez normal. - Quelque chose d'important se passe?

( innodb_print_all_deadlocks ) = innodb_print_all_deadlocks = OFF - Indique s'il faut enregistrer tous les blocages. - Si vous êtes en proie à des blocages, activez-le. Attention: Si vous avez beaucoup de blocages, cela peut écrire beaucoup sur le disque.

( Queries ) = 146,600,019 / 38829 = 3775 /sec - Requêtes (y compris dans SP)

( Created_tmp_tables ) = 43,466,450 / 38829 = 1119 /sec - Fréquence de création de tables "temporaires" dans le cadre de SELECT complexes.

( Created_tmp_disk_tables ) = 69,721 / 38829 = 1.8 /sec - Fréquence de création de disques de tables "temp" dans le cadre de SELECT complexes - augmentez tmp_table_size et max_heap_table_size. Vérifiez les règles des tables temporaires lorsque MEMORY est utilisé à la place de MyISAM. Des modifications mineures de schéma ou de requête peuvent peut-être éviter MyISAM. De meilleurs index et une reformulation des requêtes sont plus susceptibles de vous aider.

( Select_full_join ) = 341,473 / 38829 = 8.8 /sec - jointures sans index - Ajoutez des index appropriés aux tables utilisées dans JOINs.

( Select_scan ) = 20,070,358 / 38829 = 516 /sec - analyses complètes des tables - Ajouter des index/optimiser les requêtes (sauf s'il s'agit de petites tables)

( Select_scan / Com_select ) = 20,070,358 / 46219009 = 43.4% -% de sélections effectuant une analyse complète de la table. (Peut être trompé par les routines stockées.) - Ajouter des index/optimiser les requêtes

( Com_insert + Com_delete + Com_delete_multi + Com_replace + Com_update + Com_update_multi ) = (1395278 + 512340 + 0 + 1843851 + 14825066 + 9554) / 38829 = 478 /sec - écritures/sec - 50 écritures/sec + vidages de journal seront probablement au maximum de la capacité d'écriture d'E/S des disques normaux

( Com_replace ) = 1,843,851 / 38829 = 47 /sec - Envisagez de passer à INSERT ... ON DUPLICATE KEY UPDATE.

( expire_logs_days ) = 0 - Combien de temps pour purger automatiquement binlog (après ce nombre de jours) - Trop grand (ou zéro) = consomme de l'espace disque; trop petit = besoin de répondre rapidement à un crash réseau/machine. (Sans objet si log_bin = OFF)

( slave_pending_jobs_size_max / max_allowed_packet ) = 128M / 64M = 2 - Pour les threads esclaves parallèles - slave_pending_jobs_size_max ne doit pas être inférieur à max_allowed_packet

( long_query_time ) = 10 - Coupure (secondes) pour définir une requête "lente". - Suggérer 2

( back_log / max_connections ) = 1,600 / 1600 = 100.0%

( Connections ) = 1,054,868 / 38829 = 27 /sec - Connexions - Augmentez le délai d'attente; utiliser la mise en commun?

( thread_cache_size ) = 192 - Combien de processus supplémentaires conserver (non pertinent lors de l'utilisation du pool de threads) (redimensionné automatiquement à partir de 5.6.8; basé sur max_connections)

Anormalement grand:

Bytes_received = 1583925 /sec
Com_begin = 11 /sec
Com_create_db = 0.093 /HR
Com_create_function = 4.1 /HR
Com_create_procedure = 0.37 /HR
Com_create_table = 0.21 /sec
Com_delete = 13 /sec
Com_drop_procedure = 0.37 /HR
Com_insert_select = 29 /sec
Com_insert_select + Com_replace_select = 75 /sec
Com_kill = 0.74 /HR
Com_replace_select = 47 /sec
Com_show_create_db = 2.2 /HR
Com_show_create_trigger = 0.28 /HR
Com_show_events = 0.74 /HR
Com_show_storage_engines = 0.93 /HR
Com_show_warnings = 16 /HR
Com_stmt_close = 919 /sec
Com_stmt_execute = 919 /sec
Com_stmt_prepare = 919 /sec
Com_truncate = 0.16 /sec
Com_update = 381 /sec
Com_update_multi = 0.25 /sec
Handler_commit = 1638 /sec
Handler_delete = 60 /sec
Handler_external_lock = 5083 /sec
Handler_read_key = 58652 /sec
Handler_read_next = 607861 /sec
Handler_update = 40571 /sec
Innodb_buffer_pool_bytes_dirty = 1,309.3MB
Innodb_buffer_pool_pages_data = 1.18e+7
Innodb_buffer_pool_pages_dirty = 83,894
Innodb_buffer_pool_pages_flushed / max(Questions, Queries) = 0.657
Innodb_buffer_pool_pages_misc = 664,249
Innodb_buffer_pool_pages_total = 1.25e+7
Innodb_data_fsyncs = 232 /sec
Innodb_data_pending_fsyncs = 0.46 /HR
Innodb_data_read = 12400226 /sec
Innodb_data_reads = 770 /sec
Innodb_data_writes = 13131 /sec
Innodb_data_written = 88604121 /sec
Innodb_dblwr_pages_written = 2476 /sec
Innodb_dblwr_pages_written / Innodb_dblwr_writes = 81.6
Innodb_log_write_requests = 10906 /sec
Innodb_os_log_fsyncs = 87 /sec
Innodb_os_log_written / (Uptime / 3600) / innodb_log_files_in_group = 11,052.3MB
Innodb_pages_created = 57 /sec
Innodb_pages_written = 2478 /sec
Innodb_rows_deleted = 60 /sec
Innodb_rows_deleted + Innodb_rows_inserted = 658 /sec
Innodb_rows_inserted = 598 /sec
Innodb_rows_updated = 520 /sec
Max_execution_time_set = 0.0MB
Max_used_connections = 768
Select_range = 1250 /sec
Select_range / Com_select = 105.0%
Sort_range = 274 /sec
Sort_scan = 509 /sec
Table_open_cache_hits = 3826 /sec
Threads_cached = 142
Threads_connected = 366
back_log = 1,600
innodb_io_capacity_max = 20,000
innodb_max_dirty_pages_pct_lwm = 1000.0%
innodb_undo_tablespaces = 2
max_error_count = 1,024
max_length_for_sort_data = 4,096
max_user_connections = 1,500
optimizer_trace_max_mem_size = 1.05e+6
slave_pending_jobs_size_max = 128MB

Chaînes anormales:

bind_address = 0.0.0.0
event_scheduler = ON
explicit_defaults_for_timestamp = ON
have_query_cache = NO
have_ssl = YES
have_symlink = DISABLED
innodb_buffer_pool_dump_at_shutdown = ON
innodb_buffer_pool_load_at_startup = ON
innodb_fast_shutdown = 1
innodb_undo_directory = ./
innodb_undo_log_truncate = ON
master_info_repository = TABLE
optimizer_trace = enabled=off,one_line=off
optimizer_trace_features = greedy_search=on, range_optimizer=on, dynamic_range=on, repeated_subselect=on
relay_log_info_repository = TABLE
slave_rows_search_algorithms = INDEX_SCAN,HASH_SCAN
ssl_ca = ca.pem
ssl_cert = server-cert.pem
ssl_key = server-key.pem
transaction_write_set_extraction = XXHASH64
3
Rick James
UPDATE  task_s_b SET result_delivered=1, result_data='DISABLED',
        result_gathered=1
    WHERE  result_data is NULL
      AND  (assigned_counter >= 4
              OR  c_counter > 2
           ) 

Selon la distribution des valeurs dans cette table, cette requête peut causer des problèmes.

Considérez la réécriture comme deux requêtes:

UPDATE  task_s_b SET result_delivered=1, result_data='DISABLED',
        result_gathered=1
    WHERE  result_data is NULL
      AND  assigned_counter >= 4;
UPDATE  task_s_b SET result_delivered=1, result_data='DISABLED',
        result_gathered=1
    WHERE  result_data is NULL
      AND  c_counter > 2;

Et en ajoutant ces deux index composites aux colonnes dans l'ordre spécifié:

INDEX(result_data, assigned_counter)
INDEX(result_data, c_counter)

Pour en savoir plus, veuillez fournir SHOW CREATE TABLE et SHOW TABLE STATUS pour ce tableau.

Cela peut réduire les blocages et accélérer le serveur.

0
Rick James

Suggestions à considérer sans arrêt/démarrage des services de secours immédiat sur la base des données disponibles à ce moment 22 janvier 19 à 15h00 CT USA

SET GLOBAL innodb_io_capacity=10000        from your 5000 limit at this time
SET GLOBAL innodb_lru_scan_depth=100        from your 256 to reduce CPU cycles used for this function by 60% EVERY SECOND

Après l'affichage de SHOW GLOBAL VARIABLES; analyse terminée, il y aura plus de suggestions.

0
Wilson Hauck