Pourquoi les sélections simples sur InnoDB 100x sont-elles plus lentes que sur MyISAM?

33

J'ai un problème assez ennuyant. Je souhaite utiliser INNODB comme moteur de base de données principal et abandonner MyISAM, car j’ai besoin du premier pour utiliser galera-cluster à des fins de redondance.

J'ai copié (la description suit) la newbb_posttable dans une nouvelle table appelée newbb_innopostet l' ai modifiée en InnoDB. Les tables contiennent actuellement des 5,390,146entrées chacune.

L'exécution de ces sélections sur une base de données fraîchement démarrée (afin qu'aucune mise en cache ne soit impliquée à ce stade!), La base de données donne les résultats suivants (en omettant la sortie complète, veuillez noter que je ne demande même pas à la base de données de trier les résultats):

SELECT post.postid, post.attach FROM newbb_post AS post WHERE post.threadid = 51506;

.
.
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 lignes dans un ensemble (0.13 sec)
SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506;
.
.
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 lignes par séries (1 min 22.19 sec)

0,13 seconde à 86,19 secondes (!)

Je me demande pourquoi cela se produit. J'ai lu quelques réponses ici sur Stackexchange impliquant InnoDB et certaines suggèrent d'augmenter la innodb_buffer_pooltaille à 80% de la RAM installée. Cela ne résoudra pas le problème suivant: la requête initiale à un ID particulier prendra au moins 50 fois plus longtemps et bloquera la totalité de la recherche Web, mettant en file d'attente les connexions et les requêtes pour la base de données. Ensuite, le cache / tampon peut démarrer, mais il y a plus de 100 000 threads dans cette base de données. Il est donc très probable que le cache ne contiendra jamais toutes les requêtes pertinentes à traiter.

Les requêtes ci-dessus sont simples (pas de jointures) et toutes les clés sont utilisées:

EXPLAIN SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
| id | select_type | table | type | possible_keys | clé | key_len | ref | rangées | Extra |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
| 1 | SIMPLE | post | ref | threadid, threadid_2, threadid_visible_dateline | threadid | 4 | const | 120144 | |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +

C'est la table MyISAM:

CREATE TABLE `newbb_post` (
  `postid` int (10) non signé NOT NULL AUTO_INCREMENT,
  `threadid` int (10) non signé NOT NULL DEFAULT '0',
  `parentid` int (10) non signé NOT NULL DEFAULT '0',
  `username` varchar (100) NOT NULL DEFAULT '',
  `userid` int (10) non signé NOT NULL DEFAULT '0',
  `title` varchar (250) NOT NULL DEFAULT '',
  `dateline` int (10) non signé NOT NULL DEFAULT '0',
  `pagetext` mediumtext,
  `allowmilie` smallint (6) PAS NULL DEFAULT '0',
  `showsignature` smallint (6) NOT NULL DEFAULT '0',
  `ipaddress` varchar (15) NOT NULL DEFAULT '',
  `iconid` smallint (5) non signé NOT NULL DEFAULT '0',
  `visible` smallint (6) NOT NULL DEFAULT '0',
  `attach` smallint (5) non signé NOT NULL DEFAULT '0',
  `infraction` smallint (5) non signé NOT NULL DEFAULT '0',
  `reportthreadid` int (10) non signé NOT NULL DEFAULT '0',
  `importthreadid` bigint (20) NOT NULL DEFAULT '0',
  `importpostid` bigint (20) NOT NULL DEFAULT '0',
  `convert_2_utf8` int (11) NOT NULL,
  `htmlstate` enum ('off', 'on', 'on_nl2br') NOT NULL DEFAULT 'on_nl2br',
  PRIMARY KEY (`postid`),
  KEY `threadid` (` threadid`, `userid`),
  KEY `importpost_index` (` importpostid`),
  KEY `dateline` (` dateline`),
  KEY `threadid_2` (` threadid`, `visible`,` dateline`),
  CLÉ `converti_2_utf8` (` converti2_utf8`),
  KEY `threadid_visible_dateline` (` `threadid`,` visible`, `dateline`,` userid`, `postid`),
  KEY `ipaddress` (` ipaddress`),
  KEY `userid` (` userid`, `parentid`),
  KEY `user_date` (` userid`, `dateline`)
) ENGINE = MyISAM AUTO_INCREMENT = 5402802 CHARGEMENT PAR DEFAUT = latin1

et voici la table InnoDB (c'est exactement la même chose):

CREATE TABLE `newbb_innopost` (
  `postid` int (10) non signé NOT NULL AUTO_INCREMENT,
  `threadid` int (10) non signé NOT NULL DEFAULT '0',
  `parentid` int (10) non signé NOT NULL DEFAULT '0',
  `username` varchar (100) NOT NULL DEFAULT '',
  `userid` int (10) non signé NOT NULL DEFAULT '0',
  `title` varchar (250) NOT NULL DEFAULT '',
  `dateline` int (10) non signé NOT NULL DEFAULT '0',
  `pagetext` mediumtext,
  `allowmilie` smallint (6) PAS NULL DEFAULT '0',
  `showsignature` smallint (6) NOT NULL DEFAULT '0',
  `ipaddress` varchar (15) NOT NULL DEFAULT '',
  `iconid` smallint (5) non signé NOT NULL DEFAULT '0',
  `visible` smallint (6) NOT NULL DEFAULT '0',
  `attach` smallint (5) non signé NOT NULL DEFAULT '0',
  `infraction` smallint (5) non signé NOT NULL DEFAULT '0',
  `reportthreadid` int (10) non signé NOT NULL DEFAULT '0',
  `importthreadid` bigint (20) NOT NULL DEFAULT '0',
  `importpostid` bigint (20) NOT NULL DEFAULT '0',
  `convert_2_utf8` int (11) NOT NULL,
  `htmlstate` enum ('off', 'on', 'on_nl2br') NOT NULL DEFAULT 'on_nl2br',
  PRIMARY KEY (`postid`),
  KEY `threadid` (` threadid`, `userid`),
  KEY `importpost_index` (` importpostid`),
  KEY `dateline` (` dateline`),
  KEY `threadid_2` (` threadid`, `visible`,` dateline`),
  CLÉ `converti_2_utf8` (` converti2_utf8`),
  KEY `threadid_visible_dateline` (` `threadid`,` visible`, `dateline`,` userid`, `postid`),
  KEY `ipaddress` (` ipaddress`),
  KEY `userid` (` userid`, `parentid`),
  KEY `user_date` (` userid`, `dateline`)
) ENGINE = InnoDB AUTO_INCREMENT = 5402802 CHARGEMENT PAR DEFAUT = latin1

Serveur, avec 32 Go de RAM:

Version du serveur: 10.0.12-MariaDB-1 ~ trusty-wsrep-log distribution binaire de mariadb.org, wsrep_25.10.r4002

Si vous avez besoin de tous les paramètres innodb_ variables, je peux l’attacher à cet article.

Mise à jour:

J'ai abandonné TOUS les index en dehors de l'index primaire. Après, le résultat ressemblait à ceci:

.
.
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 lignes dans la série (29.74 sec)
EXPLAIN SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| id | select_type | table | type | possible_keys | clé | key_len | ref | rangées | Extra |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| 1 | SIMPLE | post | TOUS | NULL | NULL | NULL | NULL | 5909836 | Utiliser où |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
1 ligne dans le set (0.00 sec)

Après cela, je viens d'ajouter un index au mélange, threadid, les résultats sont les suivants:

.
.
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 lignes dans le jeu (11.58 sec)
EXPLAIN SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| id | select_type | table | type | possible_keys | clé | key_len | ref | rangées | Extra |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| 1 | SIMPLE | post | ref | threadid | threadid | 4 | const | 124622 | |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
1 ligne dans le set (0.00 sec)

Il est étrange que, sans index pertinents, l’analyse complète ne prenne que 29 secondes, contre 88 secondes avec les index (!).

Avec un seul index parfaitement adapté, il faut encore 11 secondes - encore beaucoup trop lentement pour une utilisation dans le monde réel.

Mise à jour 2:

J'ai installé MySQL (5.5.38-0ubuntu0.14.04.1 (Ubuntu)) sur un autre serveur avec exactement la même configuration matérielle et exactement la même base de données / tables.

Les résultats sont presque les mêmes, d’abord le tableau MyISAM:

.
.
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 lignes dans un ensemble (0,14 sec)

Et ceci est le résultat de la table InnoDB

.
.
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 lignes par série (1 min 17.63 sec)

UPDATE 3: le contenu de my.cnf

# Fichier de configuration du serveur de base de données MariaDB.
#
# Vous pouvez copier ce fichier dans l’un des fichiers suivants:
# - "/etc/mysql/my.cnf" pour définir les options globales,
# - "~ / .my.cnf" pour définir les options spécifiques à l'utilisateur.
# 
# On peut utiliser toutes les options longues prises en charge par le programme.
# Exécutez le programme avec --help pour obtenir une liste des options disponibles et avec
# --print-defaults pour voir ce qu'il comprend et utilise réellement.
#
# Pour des explications voir
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# Ceci sera transmis à tous les clients mysql
# Il a été rapporté que les mots de passe devraient être entourés de ticks / guillemets
# surtout si elles contiennent "#" des caractères ...
# N'oubliez pas de modifier /etc/mysql/debian.cnf lors de la modification de l'emplacement du socket.
[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock

# Voici les entrées pour certains programmes spécifiques
# Les valeurs suivantes supposent que vous avez au moins 32 millions de RAM

# Ceci était officiellement connu sous le nom [safe_mysqld]. Les deux versions sont actuellement analysées.
[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
nice = 0

[mysqld]
#
# * Paramètres de base
#
utilisateur = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
port = 3306
basedir = / usr
datadir = / var / lib / mysql
tmpdir = / tmp
lc_messages_dir = / usr / share / mysql
lc_messages = en_US
sauter-verrouillage externe
#
# Au lieu de sauter le réseau, la valeur par défaut est maintenant d'écouter uniquement
# localhost qui est plus compatible et n'est pas moins sécurisé.
bind-address = 127.0.0.1
#
# * Réglage fin
#
max_connections = 100
connect_timeout = 5
wait_timeout = 600
max_allowed_packet = 16M
thread_cache_size = 128
sort_buffer_size = 4M
bulk_insert_buffer_size = 16M
tmp_table_size = 32M
max_heap_table_size = 32M
#
# * MyISAM
#
# Ceci remplace le script de démarrage et vérifie les tables MyISAM si nécessaire
# la première fois qu'ils sont touchés. En cas d'erreur, faites une copie et essayez une réparation.
myisam_recover = SAUVEGARDE
key_buffer_size = 128M
# open-files-limit = 2000
table_open_cache = 400
myisam_sort_buffer_size = 512M
concurrent_insert = 2
read_buffer_size = 2M
read_rnd_buffer_size = 1M
#
# * Configuration du cache de requêtes
#
# Cache seulement les jeux de résultats minuscules, ainsi nous pouvons en insérer davantage dans le cache de requêtes.
query_cache_limit = 128K
query_cache_size = 64M
# pour des configurations plus intensives en écriture, réglez sur DEMAND ou OFF
#query_cache_type = DEMAND
#
# * Journalisation et réplication
#
# Les deux emplacements sont tournés par le cronjob.
# Sachez que ce type de journal est un facteur de perte de performances.
# À partir de la version 5.1, vous pouvez activer le journal au moment de l’exécution!
#general_log_file = /var/log/mysql/mysql.log
#general_log = 1
#
# La journalisation des erreurs va à syslog en raison de /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
#
# nous voulons connaître les erreurs de réseau et autres
log_warnings = 2
#
# Activer le journal de requête lent pour voir les requêtes avec une durée particulièrement longue
#slow_query_log [= {0 | 1}]
slow_query_log_file = /var/log/mysql/mariadb-slow.log
long_query_time = 10
#log_slow_rate_limit = 1000
log_slow_verbosity = query_plan

# log-queries-not-using-indexes
#log_slow_admin_statements
#
# Les éléments suivants peuvent être utilisés pour relire facilement les journaux de sauvegarde ou pour la réplication.
# remarque: si vous configurez un esclave de réplication, voir README.Debian à propos de
# autres paramètres que vous devrez peut-être modifier.
# id-serveur = 1
#report_host = master1
#auto_increment_increment = 2
#auto_increment_offset = 1
log_bin = / var / log / mysql / mariadb-bin
log_bin_index = /var/log/mysql/mariadb-bin.index
# pas fabuleux pour la performance, mais plus sûr
#sync_binlog = 1
expire_logs_days = 10
max_binlog_size = 100M
# des esclaves
#relay_log = / var / log / mysql / relay-bin
#relay_log_index = /var/log/mysql/relay-bin.index
#relay_log_info_file = /var/log/mysql/relay-bin.info
#log_slave_updates
#lecture seulement
#
# Si les applications le supportent, ce sql_mode plus strict empêche certaines
# erreurs comme l'insertion de dates invalides, etc.
#sql_mode = NO_ENGINE_SUBSTITUTION, TRADITIONAL
#
# * InnoDB
#
# InnoDB est activé par défaut avec un fichier de données de 10 Mo dans / var / lib / mysql /.
# Lisez le manuel pour plus d'options liées à InnoDB. Il y a beaucoup de!
default_storage_engine = InnoDB
# vous ne pouvez pas simplement changer la taille du fichier journal, nécessite une procédure spéciale
#innodb_log_file_size = 50M
innodb_buffer_pool_size = 20G
innodb_log_buffer_size = 8M
innodb_file_per_table = 1
innodb_open_files = 400
innodb_io_capacity = 400
innodb_flush_method = O_DIRECT
#
# * Fonctions de sécurité
#
# Lisez aussi le manuel, si vous voulez chroot!
# chroot = / var / lib / mysql /
#
# Pour générer des certificats SSL, je recommande l'interface graphique OpenSSL "tinyca".
#
# ssl-ca = / etc / mysql / cacert.pem
# ssl-cert = / etc / mysql / server-cert.pem
# ssl-key = / etc / mysql / server-key.pem



[mysqldump]
rapide
noms de citation
max_allowed_packet = 16M

[mysql]
# no-auto-rehash # démarrage plus rapide de mysql mais pas de complétion des onglets

[isamchk]
key_buffer = 16M

#
# * IMPORTANT: paramètres supplémentaires pouvant remplacer ceux de ce fichier!
# Les fichiers doivent se terminer par '.cnf', sinon ils seront ignorés.
#
! includedir /etc/mysql/conf.d/

Et le contenu des variables inno:

MariaDB [(none)]> AFFICHER LES VARIABLES LIKE 'inno%';
+ ------------------------------------------- + ----- ------------------- +
| Nom_variable | La valeur |
+ ------------------------------------------- + ----- ------------------- +
| innodb_adaptive_flushing | ON |
| innodb_adaptive_flushing_lwm | 10 |
| innodb_adaptive_hash_index | ON |
| innodb_adaptive_hash_index_partitions | 1 |
| innodb_adaptive_max_sleep_delay | 150000 |
| innodb_additional_mem_pool_size | 8388608 |
| innodb_api_bk_commit_interval | 5 |
| innodb_api_disable_rowlock | OFF |
| innodb_api_enable_binlog | OFF |
| innodb_api_enable_mdl | OFF |
| innodb_api_trx_level | 0 |
| innodb_autoextend_increment | 64 |
| innodb_autoinc_lock_mode | 1 |
| innodb_buffer_pool_dump_at_shutdown | OFF |
| innodb_buffer_pool_dump_now | OFF |
| innodb_buffer_pool_filename | ib_buffer_pool |
| innodb_buffer_pool_instances | 8 |
| innodb_buffer_pool_load_abort | OFF |
| innodb_buffer_pool_load_at_startup | OFF |
| innodb_buffer_pool_load_now | OFF |
| innodb_buffer_pool_populate | OFF |
| innodb_buffer_pool_size | 21474836480 |
| innodb_change_buffer_max_size | 25 |
| innodb_change_buffering | tous |
| innodb_checksum_algorithm | innodb |
| innodb_checksums | ON |
| innodb_cleaner_lsn_age_factor | high_checkpoint |
| innodb_cmp_per_index_enabled | OFF |
| innodb_commit_concurrency | 0 |
| innodb_compression_failure_threshold_pct | 5 |
| innodb_compression_level | 6 |
| innodb_compression_pad_pct_max | 50 |
| innodb_concurrency_tickets | 5000 |
| innodb_corrupt_table_action | affirmer |
| innodb_data_file_path | ibdata1: 12M: autoextend |
| innodb_data_home_dir | |
| innodb_disable_sort_file_cache | OFF |
| innodb_doublewrite | ON |
| innodb_empty_free_list_algorithm | backoff |
| innodb_fake_changes | OFF |
| innodb_fast_shutdown | 1 |
| innodb_file_format | Antilope |
| innodb_file_format_check | ON |
| innodb_file_format_max | Antilope |
| innodb_file_per_table | ON |
| innodb_flush_log_at_timeout | 1 |
| innodb_flush_log_at_trx_commit | 1 |
| innodb_flush_method | O_DIRECT |
| innodb_flush_nevisa | 1 |
| innodb_flushing_avg_loops | 30 |
| innodb_force_load_corrupted | OFF |
| innodb_force_recovery | 0 |
| innodb_foreground_preflush | exponential_backoff |
| innodb_ft_aux_table | |
| innodb_ft_cache_size | 8000000 |
| innodb_ft_enable_diag_print | OFF |
| innodb_ft_enable_stopword | ON |
| innodb_ft_max_token_size | 84 |
| innodb_ft_min_token_size | 3 |
| innodb_ft_num_word_optimize | 2000 |
| innodb_ft_result_cache_limit | 2000000000 |
| innodb_ft_server_stopword_table | |
| innodb_ft_sort_pll_degree | 2 |
| innodb_ft_total_cache_size | 640000000 |
| innodb_ft_user_stopword_table | |
| innodb_io_capacity | 400 |
| innodb_io_capacity_max | 2000 |
| innodb_kill_idle_transaction | 0 |
| innodb_large_prefix | OFF |
| innodb_lock_wait_timeout | 50 |
| innodb_locking_fake_changes | ON |
| innodb_locks_unsafe_for_binlog | OFF |
| innodb_log_arch_dir | ./ |
| innodb_log_arch_expire_sec | 0 |
| innodb_log_archive | OFF |
| innodb_log_block_size | 512 |
| innodb_log_buffer_size | 8388608 |
| innodb_log_checksum_algorithm | innodb |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 50331648 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
| innodb_lru_scan_depth | 1024 |
| innodb_max_bitmap_file_size | 104857600 |
| innodb_max_changed_pages | 1000000 |
| innodb_max_dirty_pages_pct | 75 |
| innodb_max_dirty_pages_pct_lwm | 0 |
| innodb_max_purge_lag | 0 |
| innodb_max_purge_lag_delay | 0 |
| innodb_mirrored_log_groups | 1 |
| innodb_monitor_disable | |
| innodb_monitor_enable | |
| innodb_monitor_reset | |
| innodb_monitor_reset_all | |
| innodb_old_blocks_pct | 37 |
| innodb_old_blocks_time | 1000 |
| innodb_online_alter_log_max_size | 134217728 |
| innodb_open_files | 400 |
| innodb_optimize_fulltext_only | OFF |
| innodb_page_size | 16384 |
| innodb_print_all_deadlocks | OFF |
| innodb_purge_batch_size | 300 |
| innodb_purge_threads | 1 |
| innodb_random_read_ahead | OFF |
| innodb_read_ahead_threshold | 56 |
| innodb_read_io_threads | 4 |
| innodb_read_only | OFF |
| innodb_replication_delay | 0 |
| innodb_rollback_on_timeout | OFF |
| innodb_rollback_segments | 128 |
| innodb_sched_priority_cleaner | 19 |
| innodb_show_locks_held | 10 |
| innodb_show_verbose_locks | 0 |
| innodb_sort_buffer_size | 1048576 |
| innodb_spin_wait_delay | 6 |
| innodb_stats_auto_recalc | ON |
| innodb_stats_method | nulls_equal |
| innodb_stats_on_metadata | OFF |
| innodb_stats_persistent | ON |
| innodb_stats_persistent_sample_pages | 20 |
| innodb_stats_sample_pages | 8 |
| innodb_stats_transient_sample_pages | 8 |
| innodb_status_output | OFF |
| innodb_status_output_locks | OFF |
| innodb_strict_mode | OFF |
| innodb_support_xa | ON |
| innodb_sync_array_size | 1 |
| innodb_sync_spin_loops | 30 |
| innodb_table_locks | ON |
| innodb_thread_concurrency | 0 |
| innodb_thread_sleep_delay | 10000 |
| innodb_track_changed_pages | OFF |
| innodb_undo_directory | . |
| innodb_undo_logs | 128 |
| innodb_undo_tablespaces | 0 |
| innodb_use_atomic_writes | OFF |
| innodb_use_fallocate | OFF |
| innodb_use_global_flush_log_at_trx_commit | ON |
| innodb_use_native_aio | ON |
| innodb_use_stacktrace | OFF |
| innodb_use_sys_malloc | ON |
| innodb_version | 5.6.17-65.0 |
| innodb_write_io_threads | 4 |
+ ------------------------------------------- + ----- ------------------- +
143 lignes dans le set (0.02 sec)

Le nombre de cœurs de la machine est de 8, c’est un

Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz à partir de /proc/cpuinfo

Une dernière remarque: Exécuter les requêtes avec les index suggérés par RolandoMYSQLDBA. Les requêtes ont duré environ 11-20 secondes. Je tiens à souligner qu'il est crucial pour moi (ceci est la table principale d'un babillard) que la première requête sur un ID de fil retourne en moins d'une seconde, car il y a plus de 60 000 threads et google-bots explorent en permanence ces discussions.

Jollyroger
la source
Les commentaires ne sont pas pour une discussion prolongée; cette conversation a été déplacée pour discuter .
Paul White a déclaré: GoFundMonica

Réponses:

24

VOTRE REQUÊTE

SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506;

À première vue, cette requête ne devrait toucher que 1,1597% (62510 sur 5390146) du tableau. Il devrait être rapide étant donné la distribution de clé de threadid 51506.

VÉRIFICATION DE LA RÉALITÉ

Quelle que soit la version de MySQL (Oracle, Percona, MariaDB) utilisée, aucun d’entre eux ne peut lutter contre un ennemi commun: l’architecture InnoDB.

Architecture InnoDB

INDEX CLUSTERED

N'oubliez pas que chaque entrée d'ID de thread est associée à une clé primaire. Cela signifie que lorsque vous lisez l'index, il doit effectuer une recherche de clé primaire dans ClusteredIndex (nommé en interne gen_clust_index) . Dans ClusteredIndex, chaque page InnoDB contient à la fois des données et des informations sur l’index PRIMARY KEY. Voir mon post Best of MyISAM et InnoDB pour plus d'informations.

INDEX REDONDANTS

Vous avez beaucoup de fouillis dans la table car certains index ont les mêmes colonnes de tête. MySQL et InnoDB doivent naviguer à travers le fouillis d'index pour accéder aux nœuds BTREE nécessaires. Vous devriez réduire cet encombrement en exécutant ce qui suit:

ALTER TABLE newbb_innopost
    DROP INDEX threadid,
    DROP INDEX threadid_2,
    DROP INDEX threadid_visible_dateline,
    ADD INDEX threadid_visible_dateline_index (`threadid`,`visible`,`dateline`,`userid`)
;

Pourquoi dépouiller ces index?

  • Les trois premiers index commencent par threadid
  • threadid_2et threadid_visible_datelinecommencez avec les trois mêmes colonnes
  • threadid_visible_dateline n'a pas besoin de postid car c'est la clé primaire et il est intégré

CACHING DE TAMPONS

Le pool de mémoire tampon InnoDB met en cache les pages de données et d'index. MyISAM ne met en cache que les pages d'index.

Rien que dans ce domaine, MyISAM ne perd pas de temps à mettre des données en cache. C'est parce qu'il n'est pas conçu pour mettre en cache des données. InnoDB met en cache chaque page de données et page d'index (et sa grand-mère) qu'elle touche. Si votre pool de mémoire tampon InnoDB est trop petit, vous pouvez mettre en cache des pages, invalider des pages et supprimer des pages en une seule requête.

DISPOSITION DE LA TABLE

Vous pouvez gagner de la place dans la rangée en considérant importthreadidet importpostid. Vous les avez comme BIGINTs. Ils occupent 16 octets dans le ClusteredIndex par ligne.

Tu devrais courir ça

SELECT importthreadid,importpostid FROM newbb_innopost PROCEDURE ANALYSE();

Cela recommandera les types de données que ces colonnes devraient être pour l'ensemble de données donné.

CONCLUSION

MyISAM a beaucoup moins à faire face à InnoDB, en particulier dans le domaine de la mise en cache.

Bien que vous ayez révélé la quantité de RAM ( 32GB) et la version de MySQL ( Server version: 10.0.12-MariaDB-1~trusty-wsrep-log mariadb.org binary distribution, wsrep_25.10.r4002), il existe encore d’autres pièces de ce puzzle que vous n’avez pas révélées.

  • Les paramètres InnoDB
  • Le nombre de noyaux
  • Autres paramètres de my.cnf

Si vous pouvez ajouter ces choses à la question, je peux élaborer davantage.

MISE À JOUR 2014-08-28 11:27 EDT

Vous devriez augmenter le filetage

innodb_read_io_threads = 64
innodb_write_io_threads = 16
innodb_log_buffer_size = 256M

J'envisagerais de désactiver le cache de la requête (voir mon message récent. Pourquoi query_cache_type est désactivé par défaut à partir de MySQL 5.6? )

query_cache_size = 0

Je préserverais le pool de mémoire tampon

innodb_buffer_pool_dump_at_shutdown=1
innodb_buffer_pool_load_at_startup=1

Augmentez les threads de purge (si vous utilisez DML sur plusieurs tables)

innodb_purge_threads = 4

ESSAIE !!!

RolandoMySQLDBA
la source
Je sais qu'InnoDB est censé être plus lent dans un test de vitesse pur, mais dans cette mesure? J'ai lu que l'équipe de MySQL avait travaillé dur pour combler cet écart. Nous avons toujours affaire à une augmentation d'environ 100 fois! Question - Etes-vous en train de dire que les requêtes de cette nature seraient mieux servies avec un index B-tree "direct" sans cluster (c'est-à-dire sans les données PK incluses)? Si oui, pourquoi cela n’a-t-il pas été mis en œuvre / n’est-ce pas? La fonctionnalité requise par le PO n’est certainement pas un cas d’utilisation marginal.
Vérace
Pouvez-vous ajouter un lien vers la version agrandie de cette image? Certaines parties sont difficiles à lire :-)
larmoyant
@RolandMySQLDBA merci pour l'information - J'espère que vous ne suggérez pas qu'un ralentissement de 100x est "normal" pour InnoDB ... Je pourrais vivre avec 2x ou 3x, mais 100x, c'est trop. Comme demandé, j'ai ajouté les informations manquantes à ma question :) Merci pour les explications fournies! Le nombre de cœurs de la machine est de 8.
jollyroger Le
2
@watery Voici la photo en taille réelle
RolandoMySQLDBA Le
1
Merci beaucoup pour votre aide @RolandoMySQLDBA. Malheureusement, même ces dernières modifications n'ont pas aidé, et l'InnoDB prend environ 11 à 20 secondes. J'ai essayé quelque chose en vous basant sur votre réponse: supprimer tous les index et créer un index de couverture. Cela a beaucoup aidé. Sans votre explication des index, je n'aurais pas trouvé cette solution. Je vais vérifier ta réponse et écrire une réponse moi-même en expliquant ce que j'ai fait :)
jollyroger
7

@RolandMySQLDBA a donné le bon indice pour répondre à la question. Le problème semble résider dans la requête et pour que les résultats soient restitués, chacun de ces champs doit être lu (en quelque sorte dans la base de données).

J'ai supprimé tous les index sauf le PRIMARY KEY, et inséré ce nouvel index:

ALTER TABLE newbb_innopost ADD INDEX threadid_visible_dateline_index (threadid,visible,dateline,userid,attach,ipaddress);

Ce lien explique ce qui se passe ici ( index couvrant ): Les champs de la requête qui sont interrogés postid,attachpeuvent maintenant être extraits de la clé elle-même. Cela évite de vérifier les données réelles et d’utiliser les E / S sur le disque dur.

Toutes les requêtes fonctionnent maintenant avec 0,00 secondes .. :)

Merci beaucoup à tous pour votre aide.

Edit : Le problème sous-jacent réel n'est pas résolu, je viens de le contourner avec cette technique. InnoDB a besoin de réparations sérieuses dans ce domaine.

Jollyroger
la source
Je suis confronté au même problème. La requête de myisma prend 0,01 seconde tandis que innodb prend 60 secondes, je vais essayer vos suggestions.
AMB
@AMB - 0.01s sent le cache de la requête; temps encore avec SQL_NO_CACHE.
Rick James
0

En fonction de votre requête et de votre table, il semble que vous sélectionniez des données dans une table chronologique. En tant que tel, il se peut que la durée de la requête soit lente car vous insérez simultanément?

Si ces deux choses sont vraies, puis-je suggérer de regarder dans ScaleDB comme une alternative? Vous serez toujours sur MariaDB, juste (peut-être) un moteur plus approprié.

http://www.scaledb.com - Page d'accueil http://www.scaledb.com/download-form.php - notre produit

OShadmon
la source
2
Vous devriez ajouter que l'édition majeure n'est pas gratuite.
ypercubeᵀᴹ
0

Les deux moteurs exécuteront la requête beaucoup plus rapidement avec

INDEX(threadid, attach, postid)

En effet, il s'agira d'un indice "couvrant" et fonctionnera pratiquement de la même manière (en utilisant l'indice BTree).

De plus, je dirai que ce n'est pas possible pour les deux moteurs sur un serveur "froid":

62510 rows in set (0.13 sec)

S'il vous plaît utiliser SQL_NO_CACHEchaque fois que vous exécutez des timings - nous ne voulons pas que le cache de requêtes pollue les conclusions.

Une autre approche rapide (sans regar de cache I / O):

Utilisez InnoDB et passez de PRIMARY KEY (postid)à

PRIMARY KEY(threadid, postid),
INDEX(postid)

La raison en est que toutes les lignes pertinentes seront adjacentes, ce qui nécessitera moins d'E / S, etc. Il INDEX(postid)faut rester AUTO_INCREMENTheureux. Mises en garde: Cela perturbe toutes les clés secondaires - certaines seront plus rapides, d'autres plus lentes.

Rick James
la source
0

Bien que cela ne soit pas directement applicable à @jollyroger car il a déjà le réglage correct, mais j'ai obtenu une amélioration majeure, en passant innodb_buffer_pool_sizeà 70% de ma RAM, comme expliqué dans Pourquoi myisam est-il plus lent qu'Innodb

Le premier MyISAMétait lent, mais oke. Ensuite, les InnoDBchoses ont mal tourné, semblable au 100x plus lent dans cette question et après avoir changé le réglage InnoDB, vous avez obtenu 10x plus vite ensuite MyISAM.

Mon réglage par défaut était sur 8 Mo, ce qui est beaucoup trop petit.

Hugo Delsing
la source