¿Por qué los SELECT simples en InnoDB son 100 veces más lentos que en MyISAM?


33

Tengo un problema bastante molesto. Quiero usar INNODB como mi motor de base de datos principal y renunciar a MyISAM ya que necesito el primero para usar galera-cluster para la redundancia.

Copié (la descripción sigue) la newbb_posttabla a una nueva tabla llamada newbb_innoposty la cambié a InnoDB. Las tablas actualmente contienen 5,390,146entradas cada una.

Al ejecutar estas selecciones en una base de datos recién iniciada (¡por lo que no hay almacenamiento en caché en este momento!), La base de datos produce los siguientes resultados (omitiendo la salida completa, tenga en cuenta que ni siquiera le pido a la base de datos que ordene los resultados):

SELECCIONE post.postid, post.attach FROM newbb_post COMO post DONDE post.threadid = 51506;

.
.
El | 5401593 | 0 |
El | 5401634 | 0 |
+ --------- + -------- +
62510 filas en conjunto (0,13 segundos)
SELECCIONE post.postid, post.attach FROM newbb_innopost COMO post DONDE post.threadid = 51506;
.
.
El | 5397410 | 0 |
El | 5397883 | 0 |
+ --------- + -------- +
62510 filas en conjunto (1 min 22.19 seg)

0.13 segundos a 86.19 segundos (!)

Me pregunto por qué está sucediendo esto. Leí algunas respuestas aquí en Stackexchange que involucran a InnoDB y algunas sugieren aumentar el innodb_buffer_pooltamaño al 80% de la RAM instalada. Esto no resolverá el problema, que la consulta inicial a una ID en particular tomará al menos 50 veces más tiempo y detendrá todo el servidor web, poniendo en cola las conexiones y consultas para la base de datos. Posteriormente, el caché / búfer podría iniciarse, pero hay más de 100.000 subprocesos en esta base de datos, por lo que es muy probable que el caché nunca contenga todas las consultas relevantes para ser atendidas.

Las consultas anteriores son simples (sin combinaciones) y se utilizan todas las claves:

EXPLIQUE SELECT post.postid, post.attach FROM newbb_innopost COMO post DONDE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
El | id | select_type | mesa | tipo | posibles_claves | clave | key_len | ref | filas | Extra |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
El | 1 | SIMPLE | post | ref | threadid, threadid_2, threadid_visible_dateline | threadid | 4 | const | 120144 | El |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +

Esta es la tabla MyISAM:

CREATE TABLE `newbb_post` (
  `postid` int (10) unsigned NOT NULL AUTO_INCREMENT,
  `threadid` int (10) unsigned NOT NULL DEFAULT '0',
  `parentid` int (10) unsigned NOT NULL DEFAULT '0',
  `username` varchar (100) NOT NULL DEFAULT '',
  `userid` int (10) unsigned NOT NULL DEFAULT '0',
  `title` varchar (250) NOT NULL DEFAULT '',
  `dateline` int (10) unsigned NOT NULL DEFAULT '0',
  `pagetext` mediumtext,
  `allowmilie` smallint (6) NO NULL DEFAULT '0',
  `showsignature` smallint (6) NO NULL DEFAULT '0',
  `ipaddress` varchar (15) NO NULL DEFAULT '',
  `iconid` smallint (5) unsigned NOT NULL DEFAULT '0',
  `visible` smallint (6) NO NULL DEFAULT '0',
  `attach` smallint (5) unsigned NOT NULL DEFAULT '0',
  `infraction` smallint (5) unsigned NOT NULL DEFAULT '0',
  `reportthreadid` int (10) unsigned NOT NULL DEFAULT '0',
  `importthreadid` bigint (20) NO NULL DEFAULT '0',
  `importpostid` bigint (20) NO NULL DEFAULT '0',
  `convertido_2_utf8` int (11) NO NULL,
  `htmlstate` enum ('off', 'on', 'on_nl2br') NOT NULL DEFAULT 'on_nl2br',
  CLAVE PRIMARIA (`postid`),
  KEY `threadid` (` threadid`, `userid`),
  TECLA `importpost_index` (` importpostid`),
  KEY `dateline` (` dateline`),
  KEY `threadid_2` (` threadid`, `visible`,` dateline`),
  TECLA `convertido_2_utf8` (` convertido_2_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 CHARSET POR DEFECTO = latin1

y esta es la tabla InnoDB (es exactamente la misma):

CREATE TABLE `newbb_innopost` (
  `postid` int (10) unsigned NOT NULL AUTO_INCREMENT,
  `threadid` int (10) unsigned NOT NULL DEFAULT '0',
  `parentid` int (10) unsigned NOT NULL DEFAULT '0',
  `username` varchar (100) NOT NULL DEFAULT '',
  `userid` int (10) unsigned NOT NULL DEFAULT '0',
  `title` varchar (250) NOT NULL DEFAULT '',
  `dateline` int (10) unsigned NOT NULL DEFAULT '0',
  `pagetext` mediumtext,
  `allowmilie` smallint (6) NO NULL DEFAULT '0',
  `showsignature` smallint (6) NO NULL DEFAULT '0',
  `ipaddress` varchar (15) NO NULL DEFAULT '',
  `iconid` smallint (5) unsigned NOT NULL DEFAULT '0',
  `visible` smallint (6) NO NULL DEFAULT '0',
  `attach` smallint (5) unsigned NOT NULL DEFAULT '0',
  `infraction` smallint (5) unsigned NOT NULL DEFAULT '0',
  `reportthreadid` int (10) unsigned NOT NULL DEFAULT '0',
  `importthreadid` bigint (20) NO NULL DEFAULT '0',
  `importpostid` bigint (20) NO NULL DEFAULT '0',
  `convertido_2_utf8` int (11) NO NULL,
  `htmlstate` enum ('off', 'on', 'on_nl2br') NOT NULL DEFAULT 'on_nl2br',
  CLAVE PRIMARIA (`postid`),
  KEY `threadid` (` threadid`, `userid`),
  TECLA `importpost_index` (` importpostid`),
  KEY `dateline` (` dateline`),
  KEY `threadid_2` (` threadid`, `visible`,` dateline`),
  TECLA `convertido_2_utf8` (` convertido_2_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 DEFAULT CHARSET = latin1

Servidor, con 32 GB de RAM:

versión del servidor: 10.0.12-MariaDB-1 ~ confianza wsrep-log-mariadb.org distribución binaria, wsrep_25.10.r4002

Si necesita todas las variables de ajuste innodb_, puedo adjuntar que a este mensaje.

Actualizar:

Descarté TODOS los índices aparte del índice primario, luego el resultado se veía así:

.
.
El | 5402697 | 0 |
El | 5402759 | 0 |
+ --------- + -------- +
62510 filas en conjunto (29.74 segundos)
EXPLIQUE SELECT post.postid, post.attach FROM newbb_innopost COMO post DONDE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
El | id | select_type | mesa | tipo | posibles_claves | clave | key_len | ref | filas | Extra |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
El | 1 | SIMPLE | post | TODOS | NULL | NULL | NULL | NULL | 5909836 | Usando donde |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
1 fila en conjunto (0.00 seg)

Después de esto, acabo de agregar un índice a la mezcla, threadid, los resultados fueron los siguientes:

.
.
El | 5402697 | 0 |
El | 5402759 | 0 |
+ --------- + -------- +
62510 filas en conjunto (11.58 segundos)
EXPLIQUE SELECT post.postid, post.attach FROM newbb_innopost COMO post DONDE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
El | id | select_type | mesa | tipo | posibles_claves | clave | key_len | ref | filas | Extra |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
El | 1 | SIMPLE | post | ref | threadid | threadid | 4 | const | 124622 | El |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
1 fila en conjunto (0.00 seg)

Es extraño, que sin índices relevantes, la exploración completa solo tomó 29 segundos en comparación con los 88 segundos usando índices (!).

Con solo un índice perfectamente adaptado, todavía tarda 11 segundos en completarse, aún demasiado lento para cualquier uso en el mundo real.

Actualización 2:

Configuré MySQL (5.5.38-0ubuntu0.14.04.1 (Ubuntu)) en otro servidor con exactamente la misma configuración de hardware y exactamente la misma base de datos / tablas.

Los resultados son casi los mismos, primero la tabla MyISAM:

.
.
El | 5401593 | 0 |
El | 5401634 | 0 |
+ --------- + -------- +
62510 filas en conjunto (0.14 seg)

Y este es el resultado de la tabla InnoDB

.
.
El | 5397410 | 0 |
El | 5397883 | 0 |
+ --------- + -------- +
62510 filas en conjunto (1 min 17.63 seg)

ACTUALIZACIÓN 3: los contenidos de my.cnf

# MariaDB archivo de configuración del servidor de base de datos.
# #
# Puede copiar este archivo a uno de:
# - "/etc/mysql/my.cnf" para establecer opciones globales,
# - "~ / .my.cnf" para configurar las opciones específicas del usuario.
# # 
# Uno puede usar todas las opciones largas que admite el programa.
# Ejecute el programa con --help para obtener una lista de opciones disponibles y con
# --print-defaults para ver cuál realmente entendería y usaría.
# #
# Para explicaciones ver
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# Esto se pasará a todos los clientes mysql
# Se ha informado que las contraseñas se deben incluir con marcas / comillas
# especialmente si contienen caracteres "#" ...
# Recuerde editar /etc/mysql/debian.cnf cuando cambie la ubicación del socket.
[cliente]
puerto = 3306
socket = /var/run/mysqld/mysqld.sock

# Aquí hay entradas para algunos programas específicos
# Los siguientes valores suponen que tiene al menos 32M de ram

# Esto se conocía formalmente como [safe_mysqld]. Ambas versiones están actualmente analizadas.
[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
bonito = 0

[mysqld]
# #
# * Ajustes básicos
# #
usuario = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
puerto = 3306
basedir = / usr
datadir = / var / lib / mysql
tmpdir = / tmp
lc_messages_dir = / usr / share / mysql
lc_messages = en_US
saltar-bloqueo externo
# #
# En lugar de omitir redes, el valor predeterminado ahora es escuchar solo en
# localhost, que es más compatible y no es menos seguro.
dirección-enlace = 127.0.0.1
# #
# * Sintonia FINA
# #
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
# #
# Esto reemplaza el script de inicio y comprueba las tablas MyISAM si es necesario
# la primera vez que se tocan. En caso de error, haga una copia e intente una reparación.
myisam_recover = BACKUP
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
# #
# * Configuración de la caché de consultas
# #
# Caché solo pequeños conjuntos de resultados, por lo que podemos caber más en el caché de consultas.
query_cache_limit = 128K
query_cache_size = 64M
# para configuraciones más intensivas de escritura, configure en DEMAND u OFF
#query_cache_type = DEMANDA
# #
# * Registro y replicación
# #
# Ambas ubicaciones se rotan por el cronjob.
# Tenga en cuenta que este tipo de registro es un asesino de rendimiento.
# ¡A partir de 5.1 puede habilitar el registro en tiempo de ejecución!
#general_log_file = /var/log/mysql/mysql.log
#general_log = 1
# #
# El registro de errores va a syslog debido a /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
# #
# queremos saber acerca de los errores de red y tales
log_warnings = 2
# #
# Habilite el registro lento de consultas para ver consultas con una duración especialmente larga
#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
# #
# Lo siguiente se puede usar como registros de respaldo fáciles de reproducir o para replicar.
# nota: si está configurando un esclavo de replicación, consulte README.Debian acerca de
# otras configuraciones que puede necesitar cambiar.
# id-servidor = 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
# no es fabuloso para el rendimiento, pero es más seguro
#sync_binlog = 1
expire_logs_days = 10
max_binlog_size = 100M
# esclavos
#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
#solo lectura
# #
# Si las aplicaciones lo admiten, este sql_mode más estricto evita algunos
# errores como insertar fechas inválidas, etc.
#sql_mode = NO_ENGINE_SUBSTITUTION, TRADITIONAL
# #
# * InnoDB
# #
# InnoDB está habilitado de forma predeterminada con un archivo de datos de 10 MB en / var / lib / mysql /.
# Lea el manual para más opciones relacionadas con InnoDB. ¡Hay muchos!
default_storage_engine = InnoDB
# no puede simplemente cambiar el tamaño del archivo de registro, requiere un procedimiento especial
#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
# #
# * Características de seguridad
# #
# ¡Lea también el manual si quiere chroot!
# chroot = / var / lib / mysql /
# #
# Para generar certificados SSL, recomiendo la GUI de OpenSSL "tinyca".
# #
# ssl-ca = / etc / mysql / cacert.pem
# ssl-cert = / etc / mysql / server-cert.pem
# ssl-key = / etc / mysql / server-key.pem



[mysqldump]
rápido
nombres de comillas
max_allowed_packet = 16M

[mysql]
# no-auto-rehash # inicio más rápido de mysql pero sin completar la pestaña

[isamchk]
key_buffer = 16M

# #
# * IMPORTANTE: ¡Configuraciones adicionales que pueden anular las de este archivo!
# Los archivos deben terminar con '.cnf', de lo contrario serán ignorados.
# #
! includedir /etc/mysql/conf.d/

Y el contenido de las variables inno:

MariaDB [(ninguno)]> MOSTRAR VARIABLES COMO 'inno%';
+ ------------------------------------------- + ----- ------------------- +
El | Nombre_variable | Valor |
+ ------------------------------------------- + ----- ------------------- +
El | innodb_adaptive_flushing | ON |
El | innodb_adaptive_flushing_lwm | 10 |
El | innodb_adaptive_hash_index | ON |
El | innodb_adaptive_hash_index_partitions | 1 |
El | innodb_adaptive_max_sleep_delay | 150000 |
El | innodb_additional_mem_pool_size | 8388608 |
El | innodb_api_bk_commit_interval | 5 |
El | innodb_api_disable_rowlock | OFF |
El | innodb_api_enable_binlog | OFF |
El | innodb_api_enable_mdl | OFF |
El | innodb_api_trx_level | 0 |
El | innodb_autoextend_increment | 64
El | innodb_autoinc_lock_mode | 1 |
El | innodb_buffer_pool_dump_at_shutdown | OFF |
El | innodb_buffer_pool_dump_now | OFF |
El | innodb_buffer_pool_filename | ib_buffer_pool |
El | innodb_buffer_pool_instances | 8 |
El | innodb_buffer_pool_load_abort | OFF |
El | innodb_buffer_pool_load_at_startup | OFF |
El | innodb_buffer_pool_load_now | OFF |
El | innodb_buffer_pool_populate | OFF |
El | innodb_buffer_pool_size | 21474836480 |
El | innodb_change_buffer_max_size | 25
El | innodb_change_buffering | todos |
El | innodb_checksum_algorithm | innodb |
El | innodb_checksums | ON |
El | innodb_cleaner_lsn_age_factor | high_checkpoint |
El | innodb_cmp_per_index_enabled | OFF |
El | innodb_commit_concurrency | 0 |
El | innodb_compression_failure_threshold_pct | 5 |
El | innodb_compression_level | 6 |
El | innodb_compression_pad_pct_max | 50
El | innodb_concurrency_tickets | 5000 |
El | innodb_corrupt_table_action | afirmar |
El | innodb_data_file_path | ibdata1: 12M: autoextend |
El | innodb_data_home_dir | El |
El | innodb_disable_sort_file_cache | OFF |
El | innodb_doublewrite | ON |
El | innodb_empty_free_list_algorithm | retroceso |
El | innodb_fake_changes | OFF |
El | innodb_fast_shutdown | 1 |
El | innodb_file_format | Antílope |
El | innodb_file_format_check | ON |
El | innodb_file_format_max | Antílope |
El | innodb_file_per_table | ON |
El | innodb_flush_log_at_timeout | 1 |
El | innodb_flush_log_at_trx_commit | 1 |
El | innodb_flush_method | O_DIRECT |
El | innodb_flush_neighbours | 1 |
El | innodb_flushing_avg_loops | 30
El | innodb_force_load_corrupted | OFF |
El | innodb_force_recovery | 0 |
El | innodb_foreground_preflush | exponencial_backoff |
El | innodb_ft_aux_table | El |
El | innodb_ft_cache_size | 8000000 |
El | innodb_ft_enable_diag_print | OFF |
El | innodb_ft_enable_stopword | ON |
El | innodb_ft_max_token_size | 84
El | innodb_ft_min_token_size | 3 |
El | innodb_ft_num_word_optimize | 2000 |
El | innodb_ft_result_cache_limit | 2000000000 |
El | innodb_ft_server_stopword_table | El |
El | innodb_ft_sort_pll_degree | 2 |
El | innodb_ft_total_cache_size | 640000000 |
El | innodb_ft_user_stopword_table | El |
El | innodb_io_capacity | 400 |
El | innodb_io_capacity_max | 2000 |
El | innodb_kill_idle_transaction | 0 |
El | innodb_large_prefix | OFF |
El | innodb_lock_wait_timeout | 50
El | innodb_locking_fake_changes | ON |
El | innodb_locks_unsafe_for_binlog | OFF |
El | innodb_log_arch_dir | ./ |
El | innodb_log_arch_expire_sec | 0 |
El | innodb_log_archive | OFF |
El | innodb_log_block_size | 512
El | innodb_log_buffer_size | 8388608 |
El | innodb_log_checksum_algorithm | innodb |
El | innodb_log_compressed_pages | ON |
El | innodb_log_file_size | 50331648 |
El | innodb_log_files_in_group | 2 |
El | innodb_log_group_home_dir | ./ |
El | innodb_lru_scan_depth | 1024 |
El | innodb_max_bitmap_file_size | 104857600 |
El | innodb_max_changed_pages | 1000000 |
El | innodb_max_dirty_pages_pct | 75 |
El | innodb_max_dirty_pages_pct_lwm | 0 |
El | innodb_max_purge_lag | 0 |
El | innodb_max_purge_lag_delay | 0 |
El | innodb_mirrored_log_groups | 1 |
El | innodb_monitor_disable | El |
El | innodb_monitor_enable | El |
El | innodb_monitor_reset | El |
El | innodb_monitor_reset_all | El |
El | innodb_old_blocks_pct | 37 |
El | innodb_old_blocks_time | 1000 |
El | innodb_online_alter_log_max_size | 134217728 |
El | innodb_open_files | 400 |
El | innodb_optimize_fulltext_only | OFF |
El | innodb_page_size | 16384 |
El | innodb_print_all_deadlocks | OFF |
El | innodb_purge_batch_size | 300
El | innodb_purge_threads | 1 |
El | innodb_random_read_ahead | OFF |
El | innodb_read_ahead_threshold | 56
El | 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 rows in set (0.02 sec)

The number of cores of the machine is 8, it's a

Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz as of /proc/cpuinfo

One last note: Ran the queries with the indexes suggested by RolandoMYSQLDBA, and the queries took about 11-20s each. I do want to point out that it is crucial for me (this is the main table of a bulletin board) that the first query about a threadid returns in less than a second, as there are more than 60.000 threads and google-bots constantly crawl these threads.


Comments are not for extended discussion; this conversation has been moved to chat.
Paul White says GoFundMonica

Respuestas:


24

YOUR QUERY

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

At first glance, that query should only touches 1.1597% (62510 out of 5390146) of the table. It should be fast given the key distribution of threadid 51506.

REALITY CHECK

No matter which version of MySQL (Oracle, Percona, MariaDB) you use, none of them can fight to one enemy they all have in common : The InnoDB Architecture.

InnoDB Architecture

CLUSTERED INDEX

Please keep in mind that the each threadid entry has a primary key attached. This means that when you read from the index, it must do a primary key lookup within the ClusteredIndex (internally named gen_clust_index). In the ClusteredIndex, each InnoDB page contains both data and PRIMARY KEY index info. See my post Best of MyISAM and InnoDB for more info.

REDUNDANT INDEXES

You have a lot of clutter in the table because some indexes have the same leading columns. MySQL and InnoDB has to navigate through the index clutter to get to needed BTREE nodes. You should reduced that clutter by running the following:

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`)
;

Why strip down these indexes ?

  • The first three indexes start with threadid
  • threadid_2y threadid_visible_datelinecomenzar con las mismas tres columnas
  • threadid_visible_dateline no necesita postid ya que es la CLAVE PRIMARIA y está incrustada

CACHEADO DE BUFFER

El InnoDB Buffer Pool almacena en caché datos y páginas de índice. MyISAM solo almacena en caché las páginas de índice.

Solo en esta área, MyISAM no pierde el tiempo en el almacenamiento en caché de datos. Eso es porque no está diseñado para almacenar en caché los datos. InnoDB almacena en caché cada página de datos y página de índice (y su abuela) que toca. Si su InnoDB Buffer Pool es demasiado pequeño, podría estar almacenando en caché páginas, invalidando páginas y eliminando páginas en una sola consulta.

DISEÑO DE TABLAS

Puede reducir un poco el espacio de la fila considerando importthreadidy importpostid. Los tienes como BIGINTs. Ocupan 16 bytes en el ClusteredIndex por fila.

Deberías ejecutar esto

SELECT importthreadid,importpostid FROM newbb_innopost PROCEDURE ANALYSE();

Esto recomendará qué tipos de datos deberían ser estas columnas para el conjunto de datos dado.

CONCLUSIÓN

MyISAM tiene mucho menos que lidiar con InnoDB, especialmente en el área de almacenamiento en caché.

Si bien reveló la cantidad de RAM ( 32GB) y la versión de MySQL ( Server version: 10.0.12-MariaDB-1~trusty-wsrep-log mariadb.org binary distribution, wsrep_25.10.r4002), todavía hay otras piezas de este rompecabezas que no ha revelado

  • La configuración de InnoDB
  • El número de núcleos
  • Otras configuraciones de my.cnf

Si puede agregar estas cosas a la pregunta, puedo dar más detalles.

ACTUALIZACIÓN 2014-08-28 11:27 EDT

Deberías aumentar el enhebrado

innodb_read_io_threads = 64
innodb_write_io_threads = 16
innodb_log_buffer_size = 256M

Consideraría deshabilitar el caché de consultas (consulte mi publicación reciente ¿Por qué query_cache_type está deshabilitado por defecto, inicio desde MySQL 5.6? )

query_cache_size = 0

Conservaría el grupo de búferes

innodb_buffer_pool_dump_at_shutdown=1
innodb_buffer_pool_load_at_startup=1

Aumente los hilos de purga (si hace DML en varias tablas)

innodb_purge_threads = 4

DARLE UNA OPORTUNIDAD !!!


I know that InnoDB is meant to be slower in a pure speed test, but to this extent? I've read that the MySQL team have been working hard to close this gap. We're still dealing with a ~100 fold increase! Question - are you saying that queries of this nature would be better served with a "direct" non-clustered B-tree index (i.e. not having the PK data included)? If so, why hasn't/isn't this been/being implemented? The functionality that the OP requires is definitely not a marginal use case.
Vérace

Can you add a link to full size version of that picture? Some parts are hard to read :-)
watery

@RolandMySQLDBA thanks for the information - I hope you are not suggesting that a 100x slowdown is "normal" for InnoDB... I could live with 2x or 3x, but 100x is simply too much. As requested I have added the missing information to my question :) Thanks for the explanations so far! Number of cores of the machine is 8.
jollyroger


1
Thanks a lot for your help @RolandoMySQLDBA, unfortunately even those last tweaks didn't help, and the InnoDB takes around 11-20seconds to complete. I tried something based on your answer - dropping all the indexes, and creating a covering index. That has helped a lot. Without your explanation of the indexes I wouldn't have found this solution. Going to check your answer and write an answer myself explaining what I did :)
jollyroger

7

@RolandMySQLDBA has given the right hint to answer the question. The problem seems to lie in the query and that for the results to be given back, each of those fields has to be read (somehow from the database).

I dropped all indexes but the PRIMARY KEY, and inserted this new index:

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

This link explains what happens here (covering index): The queried fields of the query which are postid,attach can now be extracted from the key itself. That saves checking the real data and using I/O to the hard disk.

All the queries now run with 0.00 seconds.. :)

Thanks a lot all for your help.

Edit: The actual underlying problem is not solved, I just circumvented it with this technique. InnoDB needs some serious fixing in this area.


i am facing the same issue. myisma query takes 0.01 seconds while innodb takes 60 seconds, gonna try your suggestions.
AMB

@AMB - 0.01s smells like the Query cache; time it again with SQL_NO_CACHE.
Rick James

0

Based on your both the query and table it seems like you are selectikg data from a time-series table. As such, it may be that the query time is slow because you are inserting simultaneously?

If those two things are true, than may I suggest looking into ScaleDB as an alternative? You will still be on MariaDB, just (maybe) a more appropriate engine.

http://www.scaledb.com - Homepage http://www.scaledb.com/download-form.php - our product


2
You should add that the major edition is not free.
ypercubeᵀᴹ

0

Both engines will run the query much faster with

INDEX(threadid, attach, postid)

This is because it will be a "covering" index, and will operate virtually the same way (using the index BTree).

Also, I will say that this is not possible for either engine on a "cold" server:

62510 rows in set (0.13 sec)

Please use SQL_NO_CACHE whenever running timings -- we don't want the Query cache to pollute the conclusions.

Another fast approach (regarless of I/O caching):

Use InnoDB, and change from PRIMARY KEY (postid) to

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

The reason is that this will cause all the relevant rows to be adjacent, thereby requiring less I/O, etc. The INDEX(postid) is to keep AUTO_INCREMENT happy. Caveat: This messes with all the secondary keys -- some will be faster, some will be slower.


0

Although not directly applicable to @jollyroger because he already has the correct setting, but I got a major improvement changing the innodb_buffer_pool_size to 70% of my RAM as explained in Why is myisam slower than Innodb

First MyISAM was slow, but oke. Then InnoDB made things bad, similar to the 100x slower in this question and after changing the setting InnoDB got 10x faster then MyISAM.

My default setting was on 8MB which is far to little.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.