Mengapa SELECT sederhana di InnoDB 100x lebih lambat daripada di MyISAM?

33

Saya punya masalah yang cukup mengganggu. Saya ingin menggunakan INNODB sebagai mesin database utama saya dan menyerah pada MyISAM karena saya membutuhkan yang pertama untuk menggunakan galera-cluster untuk redundansi.

Saya menyalin (uraian berikut) newbb_posttabel ke tabel baru yang dipanggil newbb_innopostdan mengubahnya ke InnoDB. Tabel saat ini 5,390,146masing-masing menampung entri.

Menjalankan pilihan ini pada database yang baru dimulai (jadi tidak ada caching yang terlibat pada saat ini!) Database menghasilkan hasil berikut (menghilangkan output lengkap, harap dicatat bahwa saya bahkan tidak meminta database untuk mengurutkan hasilnya):

PILIH post.postid, post.attach DARI newbb_post AS post WHERE post.threadid = 51506;

.
.
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 baris dalam pengaturan (0,13 detik)
PILIH post.postid, post.attach DARI newbb_innopost AS post WHERE post.threadid = 51506;
.
.
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 baris dalam set (1 mnt 22,19 dtk)

0,13 detik hingga 86,19 detik (!)

Saya bertanya-tanya mengapa ini terjadi. Saya memang membaca beberapa jawaban di Stackexchange yang melibatkan InnoDB dan ada yang menyarankan peningkatan innodb_buffer_poolukuran hingga 80% dari RAM yang dipasang. Ini tidak akan menyelesaikan masalah, bahwa permintaan awal untuk ID tertentu akan memakan waktu setidaknya 50x lebih lama dan menghentikan seluruh websever, mengantri koneksi dan permintaan untuk database. Setelah itu cache / buffer mungkin muncul, tetapi ada lebih dari 100.000 utas dalam database ini, sehingga sangat mungkin bahwa cache tidak akan pernah menahan semua permintaan yang relevan untuk dilayani.

Pertanyaan di atas sederhana (tidak ada yang bergabung), dan semua kunci digunakan:

JELASKAN SELECT post.postid, post.attach DARI newbb_innopost AS post WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
| id | select_type | meja | ketik | mungkin_kunci | kunci | key_len | ref | baris | Ekstra |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
| 1 | SEDERHANA | posting | ref | threadid, threadid_2, threadid_visible_dateline | threadid | 4 | const | 120144 | |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +

Ini adalah Tabel 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) BUKAN NULL DEFAULT '',
  `dateline` int (10) unsigned NOT NULL DEFAULT '0',
  mediumtext `pagetext`,
  `allowmilie` smallint (6) NOT NULL DEFAULT '0',
  `showsignature` smallint (6) NOT NULL DEFAULT '0',
  `ipaddress` varchar (15) BUKAN 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',
  `pelanggaran` 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) BUKAN NULL DEFAULT '0',
  `convert_2_utf8` int (11) TIDAK NULL,
  `htmlstate` enum ('off', 'on', 'on_nl2br') BUKAN NULL DEFAULT 'on_nl2br',
  KUNCI UTAMA (`postid`),
  KUNCI `threadid` (` threadid`, `userid`),
  KUNCI `importpost_index` (` importpostid`),
  `Dateline` KUNCI (` dateline`),
  KUNCI `threadid_2` (` threadid`, `terlihat`,` dateline`),
  KUNCI `convert_2_utf8` (` dikonversi_2_utf8`),
  KUNCI `threadid_visible_dateline` (` threadid`, `visible`,` dateline`, `userid`,` postid`),
  KUNCI `ipaddress` (` ipaddress`),
  KUNCI `userid` (` userid`, `parentid`),
  `User_date` KUNCI (` userid`, `dateline`)
) ENGINE = MyISAM AUTO_INCREMENT = 5402802 CHARSET DEFAULT = latin1

dan ini adalah Tabel InnoDB (persis sama):

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) BUKAN NULL DEFAULT '',
  `dateline` int (10) unsigned NOT NULL DEFAULT '0',
  mediumtext `pagetext`,
  `allowmilie` smallint (6) NOT NULL DEFAULT '0',
  `showsignature` smallint (6) NOT NULL DEFAULT '0',
  `ipaddress` varchar (15) BUKAN 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',
  `pelanggaran` 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) BUKAN NULL DEFAULT '0',
  `convert_2_utf8` int (11) TIDAK NULL,
  `htmlstate` enum ('off', 'on', 'on_nl2br') BUKAN NULL DEFAULT 'on_nl2br',
  KUNCI UTAMA (`postid`),
  KUNCI `threadid` (` threadid`, `userid`),
  KUNCI `importpost_index` (` importpostid`),
  `Dateline` KUNCI (` dateline`),
  KUNCI `threadid_2` (` threadid`, `terlihat`,` dateline`),
  KUNCI `convert_2_utf8` (` dikonversi_2_utf8`),
  KUNCI `threadid_visible_dateline` (` threadid`, `visible`,` dateline`, `userid`,` postid`),
  KUNCI `ipaddress` (` ipaddress`),
  KUNCI `userid` (` userid`, `parentid`),
  `User_date` KUNCI (` userid`, `dateline`)
) ENGINE = InnoDB AUTO_INCREMENT = 5402802 CHARSET DEFAULT = latin1

Server, dengan RAM 32GB:

Versi server: 10.0.12-MariaDB-1 ~ trusty-wsrep-log distribusi biner mariadb.org, wsrep_25.10.r4002

Jika Anda memerlukan semua pengaturan variabel innodb_, saya dapat melampirkannya pada posting ini.

Memperbarui:

Saya menjatuhkan SEMUA indeks terpisah dari indeks primer, setelah itu hasilnya tampak seperti ini:

.
.
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 baris di atur (29,74 dtk)
JELASKAN SELECT post.postid, post.attach DARI newbb_innopost AS post WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| id | select_type | meja | ketik | mungkin_kunci | kunci | key_len | ref | baris | Ekstra |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| 1 | SEDERHANA | posting | SEMUA | NULL | NULL | NULL | NULL | 5909836 | Menggunakan dimana |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
1 baris dalam set (0,00 dtk)

Setelah ini saya baru saja menambahkan satu indeks kembali ke mix, threadid, hasilnya adalah sebagai berikut:

.
.
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 baris di atur (11,58 detik)
JELASKAN SELECT post.postid, post.attach DARI newbb_innopost AS post WHERE post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| id | select_type | meja | ketik | mungkin_kunci | kunci | key_len | ref | baris | Ekstra |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| 1 | SEDERHANA | posting | ref | threadid | threadid | 4 | const | 124622 | |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
1 baris dalam set (0,00 dtk)

Anehnya, tanpa indeks yang relevan, pemindaian lengkap hanya membutuhkan waktu 29 detik dibandingkan dengan 88 detik menggunakan indeks (!).

Dengan hanya satu indeks yang disesuaikan dengan sempurna, proses ini masih memerlukan waktu 11 detik - masih terlalu lambat untuk penggunaan di dunia nyata.

Pembaruan 2:

Saya mengatur MySQL (5.5.38-0ubuntu0.14.04.1 (Ubuntu)) di server lain dengan konfigurasi perangkat keras yang sama persis dan database / tabel yang sama persis.

Hasilnya hampir sama, pertama Tabel MyISAM:

.
.
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 baris dalam set (0,14 detik)

Dan ini adalah hasil dari tabel InnoDB

.
.
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 baris dalam set (1 mnt 17,63 dtk)

UPDATE 3: isi my.cnf

# File konfigurasi server database MariaDB.
#
# Anda dapat menyalin file ini ke salah satu dari:
# - "/etc/mysql/my.cnf" untuk mengatur opsi global,
# - "~ / .my.cnf" untuk mengatur opsi spesifik pengguna.
# 
# Seseorang dapat menggunakan semua opsi panjang yang didukung oleh program.
# Jalankan program dengan --help untuk mendapatkan daftar opsi yang tersedia dan dengan
# --print-defaults untuk melihat yang benar-benar dimengerti dan digunakan.
#
# Untuk penjelasan, lihat
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# Ini akan diteruskan ke semua klien mysql
# Telah dilaporkan bahwa kata sandi harus diapit dengan tanda centang / tanda kutip
# terutama jika mengandung "#" karakter ...
# Ingatlah untuk mengedit /etc/mysql/debian.cnf saat mengubah lokasi soket.
[klien]
port = 3306
socket = /var/run/mysqld/mysqld.sock

# Ini adalah entri untuk beberapa program tertentu
# Nilai-nilai berikut menganggap Anda memiliki setidaknya 32 juta ram

# Ini secara resmi dikenal sebagai [safe_mysqld]. Kedua versi saat ini diuraikan.
[mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
bagus = 0

[mysqld]
#
# * Pengaturan dasar
#
user = 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
skip-external-locking
#
# Alih-alih melewatkan jaringan, default sekarang adalah hanya mendengarkan
# localhost yang lebih kompatibel dan tidak kalah aman.
bind-address = 127.0.0.1
#
# * Mencari setelan
#
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 = 16 juta
tmp_table_size = 32M
max_heap_table_size = 32M
#
# * MyISAM
#
# Ini menggantikan skrip startup dan memeriksa tabel MyISAM jika diperlukan
# pertama kali mereka disentuh. Jika terjadi kesalahan, buat salinan dan coba perbaikan.
myisam_recover = CADANGAN
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
#
# * Konfigurasi Cache Kueri
#
# Cache hanya set hasil yang kecil, sehingga kami dapat memuat lebih banyak dalam cache kueri.
query_cache_limit = 128K
query_cache_size = 64M
# untuk pengaturan penulisan yang lebih intensif, atur PERMINTAAN atau MATI
#query_cache_type = PERMINTAAN
#
# * Penebangan dan Replikasi
#
# Kedua lokasi diputar oleh cronjob.
# Perlu diketahui bahwa jenis log ini adalah pembunuh kinerja.
# Pada 5.1 Anda dapat mengaktifkan log saat runtime!
#general_log_file = /var/log/mysql/mysql.log
#general_log = 1
#
# Kesalahan logging masuk ke syslog karena /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
#
Kami ingin tahu tentang kesalahan jaringan dan semacamnya
log_warnings = 2
#
# Aktifkan log kueri lambat untuk melihat kueri dengan durasi sangat panjang
#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
#
# Hal berikut ini dapat digunakan untuk memutar ulang log cadangan atau untuk replikasi.
# note: jika Anda menyiapkan budak replikasi, lihat README.Debian tentang
# pengaturan lain yang mungkin perlu Anda ubah.
# 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
# tidak hebat untuk kinerja, tetapi lebih aman
#sync_binlog = 1
expire_logs_days = 10
max_binlog_size = 100M
# budak
#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
#read_only
#
# Jika aplikasi mendukungnya, sql_mode yang lebih ketat ini mencegah beberapa
# kesalahan seperti memasukkan tanggal yang tidak valid, dll.
#sql_mode = NO_ENGINE_SUBSTITUTION, TRADITIONAL
#
# * InnoDB
#
# InnoDB diaktifkan secara default dengan datafile 10MB di / var / lib / mysql /.
# Baca manual untuk opsi terkait InnoDB lainnya. Ada banyak!
default_storage_engine = InnoDB
# Anda tidak bisa hanya mengubah ukuran file log, memerlukan prosedur khusus
#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
#
# * Fitur keamanan
#
# Baca manual juga, jika Anda ingin chroot!
# chroot = / var / lib / mysql /
#
# Untuk menghasilkan sertifikat SSL, saya merekomendasikan OpenSSL GUI "tinyca".
#
# ssl-ca = / etc / mysql / cacert.pem
# ssl-cert = / etc / mysql / server-cert.pem
# ssl-key = / etc / mysql / server-key.pem



[mysqldump]
cepat
nama-kutipan
max_allowed_packet = 16M

[mysql]
# no-auto-rehash # lebih cepat memulai mysql tetapi tidak ada penyelesaian tab

[isamchk]
key_buffer = 16 juta

#
# * PENTING: Pengaturan tambahan yang dapat menggantikan yang dari file ini!
# File harus diakhiri dengan '.cnf', jika tidak mereka akan diabaikan.
#
! Includedir /etc/mysql/conf.d/

Dan isi dari variabel inno:

MariaDB [(tidak ada)]> TAMPILKAN VARIABEL SEPERTI 'inno%';
+ ------------------------------------------- + ----- ------------------- +
| Variable_name | Nilai |
+ ------------------------------------------- + ----- ------------------- +
| 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_inances | 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 | semua |
| 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 | menegaskan |
| 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 | Antelope |
| innodb_file_format_check | ON |
| innodb_file_format_max | Antelope |
| 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 baris diatur (0,02 dtk)

Jumlah inti mesin adalah 8, ini a

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

Satu catatan terakhir: Jalankan kueri dengan indeks yang disarankan oleh RolandoMYSQLDBA, dan kueri masing-masing memakan waktu sekitar 11-20. Saya ingin menunjukkan bahwa sangat penting bagi saya (ini adalah tabel utama dari papan buletin) bahwa permintaan pertama tentang pengembalian threadid dalam waktu kurang dari satu detik, karena ada lebih dari 60.000 utas dan google-bot terus merangkak utas ini.

Jolly Roger
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Paul White mengatakan GoFundMonica

Jawaban:

24

QUERY ANDA

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

Sepintas, kueri itu hanya menyentuh 1,1597% (62510 dari 5390146) dari tabel. Itu harus cepat diberikan kunci distribusi threadid 51506.

PENGECEKAN KENYATAAN

Tidak peduli versi MySQL (Oracle, Percona, MariaDB) mana yang Anda gunakan, tidak satu pun dari mereka yang bisa bertarung dengan satu musuh yang sama-sama mereka miliki: Arsitektur InnoDB.

Arsitektur InnoDB

INDEKS BERKELAS

Harap diingat bahwa setiap entri threadid memiliki kunci primer. Ini berarti bahwa ketika Anda membaca dari indeks, itu harus melakukan pencarian kunci utama dalam ClusteredIndex (secara internal dinamai gen_clust_index) . Dalam ClusteredIndex, setiap halaman InnoDB berisi data dan info indeks KUNCI UTAMA. Lihat posting saya Best of MyISAM dan InnoDB untuk info lebih lanjut.

INDEKSI YANG MENGGUNAKAN

Anda memiliki banyak kekacauan dalam tabel karena beberapa indeks memiliki kolom utama yang sama. MySQL dan InnoDB harus menavigasi melalui indeks kekacauan untuk mendapatkan node BTREE yang dibutuhkan. Anda harus mengurangi kekacauan itu dengan menjalankan yang berikut ini:

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

Mengapa menghapus indeks ini?

  • Tiga indeks pertama dimulai dengan threadid
  • threadid_2dan threadid_visible_datelinemulai dengan tiga kolom yang sama
  • threadid_visible_dateline tidak perlu postid karena KUNCI UTAMA dan tertanam

Caching BUFFER

Kolam Buffer InnoDB menyimpan data dan halaman indeks cache. MyISAM hanya menyimpan halaman indeks.

Hanya di area ini saja, MyISAM tidak membuang-buang waktu caching data. Itu karena itu tidak dirancang untuk menyimpan data. InnoDB melakukan cache setiap halaman data dan halaman indeks (dan neneknya) yang disentuhnya. Jika Pool Buffer InnoDB Anda terlalu kecil, Anda bisa caching halaman, membatalkan halaman, dan menghapus halaman semua dalam satu permintaan.

TATA LETAK MEJA

Anda dapat mencukur sebagian ruang dari baris dengan mempertimbangkan importthreadiddan importpostid. Anda memilikinya sebagai BIGINTs. Mereka mengambil 16 byte dalam ClusteredIndex per baris.

Anda harus menjalankan ini

SELECT importthreadid,importpostid FROM newbb_innopost PROCEDURE ANALYSE();

Ini akan merekomendasikan tipe data apa kolom ini seharusnya untuk dataset yang diberikan.

KESIMPULAN

MyISAM memiliki jauh lebih sedikit untuk bersaing daripada InnoDB, terutama di bidang caching.

Saat Anda mengungkapkan jumlah RAM ( 32GB) dan versi MySQL ( Server version: 10.0.12-MariaDB-1~trusty-wsrep-log mariadb.org binary distribution, wsrep_25.10.r4002), masih ada potongan lain untuk teka-teki ini yang belum Anda ungkapkan.

  • Pengaturan InnoDB
  • Jumlah Core
  • Pengaturan lain dari my.cnf

Jika Anda dapat menambahkan hal-hal ini ke pertanyaan, saya dapat menjelaskan lebih lanjut.

UPDATE 2014-08-28 11:27 EDT

Anda harus meningkatkan threading

innodb_read_io_threads = 64
innodb_write_io_threads = 16
innodb_log_buffer_size = 256M

Saya akan mempertimbangkan untuk menonaktifkan cache permintaan (Lihat posting saya baru-baru ini Mengapa query_cache_type dinonaktifkan secara default mulai dari MySQL 5.6? )

query_cache_size = 0

Saya akan melestarikan Buffer Pool

innodb_buffer_pool_dump_at_shutdown=1
innodb_buffer_pool_load_at_startup=1

Tingkatkan utas pembersih (jika Anda melakukan DML pada beberapa tabel)

innodb_purge_threads = 4

COBALAH !!!

RolandoMySQLDBA
sumber
Saya tahu bahwa InnoDB dimaksudkan untuk lebih lambat dalam tes kecepatan murni, tetapi sejauh ini? Saya telah membaca bahwa tim MySQL telah bekerja keras untuk menutup celah ini. Kami masih berurusan dengan peningkatan ~ 100 kali lipat! Pertanyaan - apakah Anda mengatakan bahwa pertanyaan seperti ini akan lebih baik dilayani dengan indeks B-tree non-cluster "langsung" (yaitu tidak memasukkan data PK)? Jika demikian, mengapa ini belum / sedang dilaksanakan? Fungsi yang dibutuhkan OP jelas bukan kasus penggunaan marjinal.
Vérace
Bisakah Anda menambahkan tautan ke versi ukuran penuh dari gambar itu? Beberapa bagian sulit dibaca :-)
berair
@RolandMySQLDBA terima kasih atas informasinya - Saya harap Anda tidak menyarankan perlambatan 100x adalah "normal" untuk InnoDB ... Saya bisa hidup dengan 2x atau 3x, tetapi 100x terlalu banyak. Seperti yang diminta, saya telah menambahkan informasi yang hilang ke pertanyaan saya :) Terima kasih atas penjelasannya sejauh ini! Jumlah inti mesin adalah 8.
jollyroger
2
@watery Here is the Full Size Picture: scribd.com/doc/31337494/XtraDB-InnoDB-internals-in-drawing
RolandoMySQLDBA
1
Terima kasih banyak atas bantuan Anda @RandooMySQLDBA, sayangnya bahkan tweak terakhir itu tidak membantu, dan InnoDB membutuhkan waktu sekitar 11-20 detik untuk selesai. Saya mencoba sesuatu berdasarkan jawaban Anda - menjatuhkan semua indeks, dan membuat indeks penutup. Itu sangat membantu. Tanpa penjelasan Anda tentang indeks, saya tidak akan menemukan solusi ini. Akan memeriksa jawaban Anda dan menulis jawaban sendiri menjelaskan apa yang saya lakukan :)
jollyroger
7

@RolandMySQLDBA telah memberikan petunjuk yang tepat untuk menjawab pertanyaan. Masalahnya tampaknya terletak pada kueri dan agar hasilnya dikembalikan, masing-masing bidang tersebut harus dibaca (entah bagaimana dari database).

Saya menjatuhkan semua indeks kecuali PRIMARY KEY, dan memasukkan indeks baru ini:

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

Tautan ini menjelaskan apa yang terjadi di sini ( mencakup indeks ): Bidang kueri dari kueri yangpostid,attach sekarang dapat diekstraksi dari kunci itu sendiri. Itu menghemat memeriksa data nyata dan menggunakan I / O ke hard disk.

Semua pertanyaan sekarang dijalankan dengan 0,00 detik .. :)

Terima kasih banyak atas bantuan Anda.

Sunting : Masalah mendasar yang sebenarnya tidak terpecahkan, saya hanya mengelak dengan teknik ini. InnoDB membutuhkan beberapa perbaikan serius di bidang ini.

Jolly Roger
sumber
saya menghadapi masalah yang sama. permintaan myisma membutuhkan waktu 0,01 detik sementara innodb membutuhkan waktu 60 detik, akan mencoba saran Anda.
AMB
@ AMB - 0,01 berbau seperti cache Query; mengatur waktu lagi dengan SQL_NO_CACHE.
Rick James
0

Berdasarkan kueri dan tabel Anda, sepertinya Anda adalah data selectikg dari tabel seri waktu. Dengan demikian, mungkin waktu kueri lambat karena Anda memasukkan secara bersamaan?

Jika kedua hal itu benar, daripada mungkin saya sarankan melihat ScaleDB sebagai alternatif? Anda masih menggunakan MariaDB, hanya (mungkin) mesin yang lebih tepat.

http://www.scaledb.com - Beranda http://www.scaledb.com/download-form.php - produk kami

Oshadmon
sumber
2
Anda harus menambahkan bahwa edisi utama tidak gratis.
ypercubeᵀᴹ
0

Kedua mesin akan menjalankan kueri dengan lebih cepat

INDEX(threadid, attach, postid)

Ini karena itu akan menjadi indeks "meliputi", dan akan beroperasi dengan cara yang hampir sama (menggunakan indeks BTree).

Juga, saya akan mengatakan bahwa hal ini tidak mungkin untuk baik mesin pada server "dingin":

62510 rows in set (0.13 sec)

Silakan gunakan SQL_NO_CACHEsetiap kali menjalankan timing - kami tidak ingin cache Query mencemari kesimpulan.

Pendekatan cepat lainnya (tidak bisa melakukan caching I / O):

Gunakan InnoDB, dan ubah dari PRIMARY KEY (postid)menjadi

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

Alasannya adalah bahwa ini akan menyebabkan semua baris yang relevan berdekatan, sehingga membutuhkan lebih sedikit I / O, dll. INDEX(postid)Adalah untuk tetap AUTO_INCREMENTbahagia. Peringatan: Ini mengacaukan semua kunci sekunder - beberapa akan lebih cepat, beberapa akan lebih lambat.

Rick James
sumber
0

Meskipun tidak secara langsung berlaku untuk @ jollyroger karena dia sudah memiliki pengaturan yang benar, tetapi saya mendapat peningkatan besar mengubah innodb_buffer_pool_size70% dari RAM saya seperti yang dijelaskan dalam Mengapa myisam lebih lambat dari Innodb

Pertama MyISAMlambat, tapi oke. Kemudian InnoDBmembuat hal-hal buruk, mirip dengan 100x lebih lambat dalam pertanyaan ini dan setelah mengubah pengaturan InnoDBmenjadi 10x lebih cepat MyISAM.

Pengaturan default saya adalah 8MB yang jauh dari sedikit.

Hugo Delsing
sumber