Eu tenho um problema bastante irritante. Quero usar o INNODB como meu principal mecanismo de banco de dados e desistir do MyISAM, pois preciso do primeiro para usar o galera-cluster para redundância.
Copiei (descrição a seguir) a newbb_post
tabela para uma nova tabela chamada newbb_innopost
e alterei para InnoDB. As tabelas atualmente contêm 5,390,146
entradas cada.
Ao executar essas seleções em um banco de dados recém-iniciado (portanto, nenhum cache está envolvido neste momento!), O banco de dados produz os seguintes resultados (omitindo a saída completa, observe que eu nem peço ao banco de dados para classificar os resultados):
SELECT post.postid, post.attach FROM newbb_post AS post WHERE post.threadid = 51506; . . | 5401593 0 | 5401634 0 + --------- + -------- + 62510 linhas no conjunto (0,13 s)
SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506; . . | 5397410 0 | 5397883 0 + --------- + -------- + 62510 linhas em conjunto (1 min 22,19 s)
0,13 segundos a 86,19 segundos (!)
Eu estou querendo saber por que isso está acontecendo. Eu li algumas respostas aqui no Stackexchange envolvendo o InnoDB e algumas sugerem aumentar o innodb_buffer_pool
tamanho para 80% da RAM instalada. Isso não resolverá o problema: a consulta inicial a um ID específico levará pelo menos 50 vezes mais tempo e paralisará toda a web, enfileirando conexões e consultas para o banco de dados. Posteriormente, o cache / buffer pode entrar, mas há mais de 100.000 threads neste banco de dados, portanto, é muito provável que o cache nunca retenha todas as consultas relevantes a serem atendidas.
As consultas acima são simples (sem junções) e todas as chaves são usadas:
EXPLAIN SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506; + ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- + | id | select_type | mesa | tipo | possible_keys | chave key_len | ref linhas | Extra | + ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- + | 1 | SIMPLES | postar | ref threadid, threadid_2, threadid_visible_dateline | threadid | 4 const 120144 | + ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
Esta é a tabela MyISAM:
CREATE TABLE `newbb_post` ( `postid` int (10) não assinado NOT NULL AUTO_INCREMENT, `threadid` int (10) não assinado NOT NULL DEFAULT '0', `parentid` int (10) não assinado NOT NULL DEFAULT '0', `username` varchar (100) NÃO NULL PADRÃO '', `userid` int (10) não assinado NOT NULL DEFAULT '0', `title` varchar (250) NÃO NULL PADRÃO '', `dateline` int (10) não assinado NOT NULL DEFAULT '0', `pagetext` texto médio, `allowmilie` smallint (6) NÃO NULL PADRÃO '0', `showsignature` smallint (6) NÃO NULL DEFAULT '0', `ipaddress` varchar (15) NÃO NULL PADRÃO '', `iconid` smallint (5) não assinado NOT NULL DEFAULT '0', `visible` smallint (6) NÃO NULL PADRÃO '0', `attach` smallint (5) não assinado NOT NULL DEFAULT '0', `infraction` smallint (5) não assinado NOT NULL DEFAULT '0', `reportthreadid` int (10) não assinado NOT NULL DEFAULT '0', `importthreadid` bigint (20) NÃO NULL PADRÃO '0', `importpostid` bigint (20) NÃO NULL DEFAULT '0', `convert_2_utf8` int (11) NÃO NULL, `htmlstate` enum ('off', 'on', 'on_nl2br') NÃO NULL PADRÃO 'on_nl2br', CHAVE PRIMÁRIA (`postid`), KEY `threadid` (` threadid`, `userid`), KEY `importpost_index` (` importpostid`), KEY `linha de dados` (` linha de dados`), KEY `threadid_2` (` threadid`, `visible`,` dateline`), KEY `convertido_2_utf8` (` convertido_2_utf8`), KEY `threadid_visible_dateline` (` threadid`, `visible`,` dateline`, `userid`,` postid`), KEY `ipaddress` (` ipaddress`), KEY `userid` (` userid`, `parentid`), KEY `data_do_usuário` (` ID do usuário`, `linha de dados`) ) MOTOR = MyISAM AUTO_INCREMENT = 5402802 CHARSET PADRÃO = latin1
e esta é a tabela InnoDB (é exatamente a mesma):
CREATE TABLE `newbb_innopost` ( `postid` int (10) não assinado NOT NULL AUTO_INCREMENT, `threadid` int (10) não assinado NOT NULL DEFAULT '0', `parentid` int (10) não assinado NOT NULL DEFAULT '0', `username` varchar (100) NÃO NULL PADRÃO '', `userid` int (10) não assinado NOT NULL DEFAULT '0', `title` varchar (250) NÃO NULL PADRÃO '', `dateline` int (10) não assinado NOT NULL DEFAULT '0', `pagetext` texto médio, `allowmilie` smallint (6) NÃO NULL PADRÃO '0', `showsignature` smallint (6) NÃO NULL DEFAULT '0', `ipaddress` varchar (15) NÃO NULL PADRÃO '', `iconid` smallint (5) não assinado NOT NULL DEFAULT '0', `visible` smallint (6) NÃO NULL PADRÃO '0', `attach` smallint (5) não assinado NOT NULL DEFAULT '0', `infraction` smallint (5) não assinado NOT NULL DEFAULT '0', `reportthreadid` int (10) não assinado NOT NULL DEFAULT '0', `importthreadid` bigint (20) NÃO NULL PADRÃO '0', `importpostid` bigint (20) NÃO NULL DEFAULT '0', `convert_2_utf8` int (11) NÃO NULL, `htmlstate` enum ('off', 'on', 'on_nl2br') NÃO NULL PADRÃO 'on_nl2br', CHAVE PRIMÁRIA (`postid`), KEY `threadid` (` threadid`, `userid`), KEY `importpost_index` (` importpostid`), KEY `linha de dados` (` linha de dados`), KEY `threadid_2` (` threadid`, `visible`,` dateline`), KEY `convertido_2_utf8` (` convertido_2_utf8`), KEY `threadid_visible_dateline` (` threadid`, `visible`,` dateline`, `userid`,` postid`), KEY `ipaddress` (` ipaddress`), KEY `userid` (` userid`, `parentid`), KEY `data_do_usuário` (` ID do usuário`, `linha de dados`) ) MOTOR = InnoDB AUTO_INCREMENT = 5402802 CHARSET PADRÃO = latin1
Servidor, com 32 GB de RAM:
Versão do servidor: 10.0.12-MariaDB-1 ~ distribuição binária triad-wsrep-log mariadb.org, wsrep_25.10.r4002
Se você precisar de todas as configurações de variáveis innodb_, posso anexar isso a este post.
Atualizar:
Larguei TODOS os índices além do índice primário, depois o resultado ficou assim:
. . | 5402697 | 0 | 5402759 0 + --------- + -------- + 62510 linhas no conjunto (29,74 seg)
EXPLAIN SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506; + ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- + | id | select_type | mesa | tipo | possible_keys | chave key_len | ref linhas | Extra | + ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- + | 1 | SIMPLES | postar | TUDO | NULL NULL NULL NULL 5909836 Usando onde | + ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- + 1 linha no conjunto (0,00 s)
Depois disso, eu apenas adicionei um índice de volta ao mix, threadid, os resultados foram os seguintes:
. . | 5402697 | 0 | 5402759 0 + --------- + -------- + 62510 linhas no conjunto (11,58 seg)
EXPLAIN SELECT post.postid, post.attach FROM newbb_innopost AS post WHERE post.threadid = 51506; + ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- + | id | select_type | mesa | tipo | possible_keys | chave key_len | ref linhas | Extra | + ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- + | 1 | SIMPLES | postar | ref threadid | threadid | 4 const 124622 | + ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- + 1 linha no conjunto (0,00 s)
Estranho é que, sem índices relevantes, a verificação completa levou apenas 29 segundos em comparação com os 88 segundos usando índices (!).
Com apenas um índice perfeitamente adaptado, ainda está demorando 11 segundos para ser concluído - ainda é muito lento para qualquer uso no mundo real.
Atualização 2:
Eu configurei o MySQL (5.5.38-0ubuntu0.14.04.1 (Ubuntu)) em outro servidor com exatamente a mesma configuração de hardware e exatamente o mesmo banco de dados / tabelas.
Os resultados são quase os mesmos, primeiro a tabela MyISAM:
. . | 5401593 0 | 5401634 0 + --------- + -------- + 62510 linhas no conjunto (0,14 s)
E este é o resultado da tabela InnoDB
. . | 5397410 0 | 5397883 0 + --------- + -------- + 62510 linhas em conjunto (1 min 17,63 seg)
ATUALIZAÇÃO 3: o conteúdo do my.cnf
# Arquivo de configuração do servidor de banco de dados MariaDB. # # Você pode copiar este arquivo para um dos seguintes: # - "/etc/mysql/my.cnf" para definir opções globais, # - "~ / .my.cnf" para definir opções específicas do usuário. # # Pode-se usar todas as opções longas suportadas pelo programa. # Execute o programa com --help para obter uma lista de opções disponíveis e com # --print-default para ver qual ele realmente entenderia e usaria. # # Para explicações, consulte # http://dev.mysql.com/doc/mysql/en/server-system-variables.html # Isso será passado para todos os clientes mysql # Foi relatado que as senhas devem ser colocadas com carrapatos / aspas # especialmente se eles contêm caracteres "#" ... # Lembre-se de editar o /etc/mysql/debian.cnf ao alterar a localização do soquete. [cliente] port = 3306 socket = /var/run/mysqld/mysqld.sock # Aqui estão as entradas para alguns programas específicos # Os seguintes valores assumem que você tem pelo menos 32M de RAM # Isso foi formalmente conhecido como [safe_mysqld]. As duas versões estão atualmente analisadas. [mysqld_safe] socket = /var/run/mysqld/mysqld.sock nice = 0 [mysqld] # # * Configurações básicas # usuário = 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 = pt_BR pular bloqueio externo # # Em vez de pular a rede, o padrão agora é ouvir apenas em # localhost, que é mais compatível e não é menos seguro. endereço de ligação = 127.0.0.1 # # * Afinação # 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 # # Isso substitui o script de inicialização e verifica as tabelas MyISAM, se necessário # na primeira vez em que são tocados. Em caso de erro, faça uma cópia e tente um reparo. 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 # # * Configuração do cache de consulta # # Armazenar em cache apenas pequenos conjuntos de resultados, para que possamos ajustar mais no cache de consultas. query_cache_limit = 128K query_cache_size = 64M # para configurações mais intensivas de gravação, defina como DEMAND ou OFF #query_cache_type = DEMANDA # # * Log e replicação # # Ambos os locais são rotacionados pelo cronjob. # Esteja ciente de que esse tipo de log é prejudicial ao desempenho. # A partir da versão 5.1, você pode ativar o log em tempo de execução! #general_log_file = /var/log/mysql/mysql.log #general_log = 1 # # O log de erros vai para o syslog devido a /etc/mysql/conf.d/mysqld_safe_syslog.cnf. # # queremos saber sobre erros de rede e tais log_warnings = 2 # # Ative o log de consultas lento para ver consultas com duração especialmente longa #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 # # O seguinte pode ser usado como fácil para reproduzir logs de backup ou para replicação. # note: se você estiver configurando um escravo de replicação, consulte README.Debian sobre # outras configurações que você precise alterar. # server-id = 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 # não fabuloso por desempenho, mas mais seguro #sync_binlog = 1 expire_logs_days = 10 max_binlog_size = 100M # escravos #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 #somente leitura # # Se os aplicativos suportarem, esse sql_mode mais restrito evita que alguns # erros como inserir datas inválidas etc. #sql_mode = NO_ENGINE_SUBSTITUTION, TRADICIONAL # # * InnoDB # # O InnoDB é ativado por padrão com um arquivo de dados de 10 MB em / var / lib / mysql /. # Leia o manual para obter mais opções relacionadas ao InnoDB. Há muitos! default_storage_engine = InnoDB # você não pode simplesmente alterar o tamanho do arquivo de log, requer procedimento 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 # # * Recursos de segurança # # Leia o manual também, se você quiser chroot! # chroot = / var / lib / mysql / # # Para gerar certificados SSL, recomendo a interface gráfica do usuário 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 aspas max_allowed_packet = 16M [mysql] # no-auto-rehash # início mais rápido do mysql, mas nenhuma conclusão de guia [isamchk] key_buffer = 16M # # * IMPORTANTE: Configurações adicionais que podem substituir as deste arquivo! # Os arquivos devem terminar com '.cnf', caso contrário, serão ignorados. # ! includedir /etc/mysql/conf.d/
E o conteúdo das variáveis inno:
MariaDB [(nenhum)]> MOSTRAR VARIÁVEIS COMO 'inno%'; + ------------------------------------------- + ----- ------------------- + | Nome da variável | Valor + ------------------------------------------- + ----- ------------------- + | 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 | tudo | | 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 | afirmar | | 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 | Antílope | | innodb_file_format_check | ON | | innodb_file_format_max | Antílope | | 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_neighbors | 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 linhas em conjunto (0,02 s)
O número de núcleos da máquina é 8, é um
Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz
a partir de /proc/cpuinfo
Uma última observação: executou as consultas com os índices sugeridos pelo RolandoMYSQLDBA e as consultas demoraram cerca de 11 a 20s cada. Quero ressaltar que é crucial para mim (esta é a tabela principal de um quadro de avisos) que a primeira consulta sobre um threadid retorne em menos de um segundo, pois existem mais de 60.000 threads e o google-bots constantemente rastreia esses tópicos.