Ho un problema piuttosto fastidioso. Voglio usare INNODB come motore di database principale e rinunciare a MyISAM poiché ho bisogno del primo per usare il cluster galera per la ridondanza.
Ho copiato (la descrizione segue) la newbb_post
tabella in una nuova tabella chiamata newbb_innopost
e l' ho cambiata in InnoDB. Le tabelle contengono attualmente 5,390,146
voci ciascuna.
Eseguendo queste selezioni su un database appena avviato (quindi a questo punto non è coinvolta alcuna memorizzazione nella cache!) Il database produce i seguenti risultati (omettendo l'output completo, si noti che non chiedo nemmeno al database di ordinare i risultati):
SELEZIONA post.postid, post.attach DA newbb_post AS post DOVE post.threadid = 51506; . . | 5401593 | 0 | | 5401634 | 0 | + --------- + -------- + 62510 righe nel set (0,13 sec)
SELEZIONA post.postid, post.attach DA newbb_innopost AS post DOVE post.threadid = 51506; . . | 5397410 | 0 | | 5397883 | 0 | + --------- + -------- + 62510 righe nel set (1 min 22,19 sec)
Da 0,13 secondi a 86,19 secondi (!)
Mi chiedo perché questo stia accadendo. Ho letto alcune risposte qui su Stackexchange che coinvolge InnoDB e alcuni suggeriscono di aumentare le innodb_buffer_pool
dimensioni all'80% della RAM installata. Ciò non risolverà il problema, poiché la query iniziale su un determinato ID richiederà almeno 50 volte più tempo e bloccherà l'intero websever, accodando connessioni e query per il database. Successivamente la cache / buffer potrebbe avviarsi, ma ci sono oltre 100.000 thread in questo database, quindi è molto probabile che la cache non conterrà mai tutte le query pertinenti da servire.
Le query sopra sono semplici (nessun join) e vengono utilizzate tutte le chiavi:
SPIEGA SELEZIONA post.postid, post.attach DA newbb_innopost AS post DOVE post.threadid = 51506; + ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- + | id | select_type | tabella | digitare | possible_keys | chiave | key_len | rif | righe | Extra | + ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- + | 1 | SEMPLICE | posta | rif | threadid, threadid_2, threadid_visible_dateline | threadid | 4 | const | 120144 | | + ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
Questa è la tabella 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', medium 'pagetext`, `allowmilie` smallint (6) NON NULL DEFAULT '0', `showsignature` smallint (6) NOT NULL DEFAULT '0', `ipaddress` varchar (15) NOT NULL DEFAULT '', `iconid` smallint (5) unsigned NOT NULL DEFAULT '0', `visible` smallint (6) NOT 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) NOT NULL DEFAULT '0', `importpostid` bigint (20) NOT NULL DEFAULT '0', `convert_2_utf8` int (11) NOT NULL, `htmlstate` enum ('off', 'on', 'on_nl2br') NON NULL DEFAULT 'on_nl2br', CHIAVE PRIMARIA (`postid`), KEY `threadid` (` threadid`, `userid`), KEY `importpost_index` (` importpostid`), KEY `dateline` (` dateline`), KEY `threadid_2` (` threadid`, `visible`,` dateline`), KEY `convert_2_utf8` (` convert_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 CARATTERISTICHE PREDEFINITE = latin1
e questa è la tabella InnoDB (è esattamente la stessa):
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', medium 'pagetext`, `allowmilie` smallint (6) NON NULL DEFAULT '0', `showsignature` smallint (6) NOT NULL DEFAULT '0', `ipaddress` varchar (15) NOT NULL DEFAULT '', `iconid` smallint (5) unsigned NOT NULL DEFAULT '0', `visible` smallint (6) NOT 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) NOT NULL DEFAULT '0', `importpostid` bigint (20) NOT NULL DEFAULT '0', `convert_2_utf8` int (11) NOT NULL, `htmlstate` enum ('off', 'on', 'on_nl2br') NON NULL DEFAULT 'on_nl2br', CHIAVE PRIMARIA (`postid`), KEY `threadid` (` threadid`, `userid`), KEY `importpost_index` (` importpostid`), KEY `dateline` (` dateline`), KEY `threadid_2` (` threadid`, `visible`,` dateline`), KEY `convert_2_utf8` (` convert_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 CARATTERISTICHE PREDEFINITE = latin1
Server, con 32 GB di RAM:
Versione server: 10.0.12-MariaDB-1 ~ trusty-wsrep-log mariadb.org distribuzione binaria, wsrep_25.10.r4002
Se hai bisogno di tutte le impostazioni delle variabili innodb_, posso collegarle a questo post.
Aggiornare:
Ho lasciato cadere TUTTI gli indici dall'indice primario, in seguito il risultato è stato questo:
. . | 5402697 | 0 | | 5402759 | 0 | + --------- + -------- + 62510 righe nel set (29,74 sec)
SPIEGA SELEZIONA post.postid, post.attach DA newbb_innopost AS post DOVE post.threadid = 51506; + ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- + | id | select_type | tabella | digitare | possible_keys | chiave | key_len | rif | righe | Extra | + ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- + | 1 | SEMPLICE | posta | TUTTO | NULL | NULL | NULL | NULL | 5909836 | Utilizzando dove | + ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- + 1 riga nel set (0,00 sec)
Dopo questo ho appena aggiunto un indice al mix, threadid, i risultati sono stati i seguenti:
. . | 5402697 | 0 | | 5402759 | 0 | + --------- + -------- + 62510 righe nel set (11,58 sec)
SPIEGA SELEZIONA post.postid, post.attach DA newbb_innopost AS post DOVE post.threadid = 51506; + ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- + | id | select_type | tabella | digitare | possible_keys | chiave | key_len | rif | righe | Extra | + ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- + | 1 | SEMPLICE | posta | rif | threadid | threadid | 4 | const | 124622 | | + ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- + 1 riga nel set (0,00 sec)
È strano che, senza indici rilevanti, la scansione completa abbia richiesto solo 29 secondi rispetto agli 88 secondi usando gli indici (!).
Con un solo indice perfettamente su misura ci vogliono ancora 11 secondi per completarlo - ancora troppo lento per qualsiasi utilizzo nel mondo reale.
Aggiornamento 2:
Ho installato MySQL (5.5.38-0ubuntu0.14.04.1 (Ubuntu)) su un altro server con la stessa identica configurazione hardware e esattamente lo stesso database / tabelle.
I risultati sono quasi gli stessi, prima la tabella MyISAM:
. . | 5401593 | 0 | | 5401634 | 0 | + --------- + -------- + 62510 righe nel set (0,14 sec)
E questo è il risultato della tabella InnoDB
. . | 5397410 | 0 | | 5397883 | 0 | + --------- + -------- + 62510 righe nel set (1 min 17,63 sec)
AGGIORNAMENTO 3: i contenuti di my.cnf
# File di configurazione del server di database MariaDB. # # È possibile copiare questo file in uno di: # - "/etc/mysql/my.cnf" per impostare opzioni globali, # - "~ / .my.cnf" per impostare opzioni specifiche dell'utente. # # È possibile utilizzare tutte le opzioni lunghe supportate dal programma. # Esegui il programma con --help per ottenere un elenco di opzioni disponibili e con # --print-default per vedere quale effettivamente comprenderebbe e userebbe. # # Per le spiegazioni vedere # http://dev.mysql.com/doc/mysql/en/server-system-variables.html # Questo verrà passato a tutti i client mysql # È stato segnalato che le password devono essere racchiuse tra segni di spunta / virgolette # specialmente se contengono caratteri "#" ... # Ricordarsi di modificare /etc/mysql/debian.cnf quando si cambia la posizione del socket. [cliente] porta = 3306 socket = /var/run/mysqld/mysqld.sock # Ecco le voci per alcuni programmi specifici # I seguenti valori presuppongono che tu abbia almeno 32 MB di RAM # Questo era formalmente noto come [safe_mysqld]. Entrambe le versioni sono attualmente analizzate. [Mysqld_safe] socket = /var/run/mysqld/mysqld.sock bello = 0 [Mysqld] # # * Impostazioni di base # utente = mysql pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock porta = 3306 basedir = / usr datadir = / var / lib / mysql tmpdir = / tmp lc_messages_dir = / usr / share / mysql lc_messages = en_US saltare-external-bloccaggio # # Invece di saltare la rete, il valore predefinito è ora solo per l'ascolto # localhost che è più compatibile e non meno sicuro. bind-address = 127.0.0.1 # # * Ritocchi # 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 # # Questo sostituisce lo script di avvio e controlla le tabelle MyISAM se necessario # la prima volta che vengono toccati. In caso di errore, copia e prova a riparare. 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 # # * Configurazione cache query # # Cache solo minuscoli set di risultati, in modo che possiamo adattarci maggiormente alla cache delle query. query_cache_limit = 128K query_cache_size = 64M # per impostazioni più intense di scrittura, impostare su RICHIESTA o DISATTIVATO #query_cache_type = DOMANDA # # * Registrazione e replica # # Entrambe le posizioni vengono ruotate dal cronjob. # Tenere presente che questo tipo di registro è un killer delle prestazioni. # A partire da 5.1 è possibile abilitare il registro in fase di esecuzione! #general_log_file = /var/log/mysql/mysql.log #general_log = 1 # # La registrazione degli errori va su syslog a causa di /etc/mysql/conf.d/mysqld_safe_syslog.cnf. # # vogliamo conoscere errori di rete e simili log_warnings = 2 # # Abilitare il registro delle query lente per visualizzare le query con una durata particolarmente lunga #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-query-non-uso-indici #log_slow_admin_statements # # È possibile utilizzare quanto segue per riprodurre facilmente i registri di backup o per la replica. # nota: se si sta configurando uno slave di replica, consultare README.Debian about # altre impostazioni che potresti dover modificare. # 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 # non favoloso per le prestazioni, ma più sicuro #sync_binlog = 1 expire_logs_days = 10 max_binlog_size = 100M # schiavi #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 #sola lettura # # Se le applicazioni lo supportano, questo sql_mode più rigoroso ne impedisce alcune # errori come l'inserimento di date non valide ecc. #sql_mode = NO_ENGINE_SUBSTITUTION, TRADIZIONALE # # * InnoDB # # InnoDB è abilitato di default con un file di dati da 10 MB in / var / lib / mysql /. # Leggi il manuale per ulteriori opzioni relative a InnoDB. Ci sono molti! default_storage_engine = InnoDB # non puoi semplicemente modificare le dimensioni del file di registro, richiede una procedura speciale #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 # # * Caratteristiche di sicurezza # # Leggi anche il manuale, se vuoi chroot! # chroot = / var / lib / mysql / # # Per generare certificati SSL, consiglio la GUI di OpenSSL "tinyca". # # ssl-ca = / etc / mysql / cacert.pem # ssl-cert = / etc / mysql / server-cert.pem # ssl-key = / etc / mysql / server-key.pem [Mysqldump] Presto quote-nomi max_allowed_packet = 16M [Mysql] # no-auto-rehash # avvio più veloce di mysql ma nessun completamento della scheda [Isamchk] key_buffer = 16M # # * IMPORTANTE: impostazioni aggiuntive che possono sovrascrivere quelle da questo file! # I file devono terminare con '.cnf', altrimenti verranno ignorati. # ! includedir /etc/mysql/conf.d/
E il contenuto delle variabili inno:
MariaDB [(nessuno)]> MOSTRA VARIABILI COME 'inno%'; + ------------------------------------------- + ----- ------------------- + | Nome variabile | Valore | + ------------------------------------------- + ----- ------------------- + | 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 | tutto | | 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 | affermare | | 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_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 righe in set (0,02 sec)
Il numero di core della macchina è 8, è a
Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz
come di /proc/cpuinfo
Un'ultima nota: ho eseguito le query con gli indici suggeriti da RolandoMYSQLDBA e le query hanno richiesto circa 11-20 secondi ciascuna. Voglio sottolineare che per me è cruciale (questa è la tabella principale di una bacheca) che la prima query su un threadid ritorna in meno di un secondo, poiché ci sono più di 60.000 thread e google-bot strisciano costantemente questi fili.