Почему простые SELECT на InnoDB в 100 раз медленнее, чем на MyISAM?

33

У меня довольно неприятная проблема. Я хочу использовать INNODB в качестве основного механизма базы данных и отказаться от MyISAM, так как мне нужен первый для использования galera-cluster для избыточности.

Я скопировал (описание следует) newbb_postтаблицу в новую таблицу с именем newbb_innopostи изменил ее на InnoDB. Таблицы в настоящее время содержат 5,390,146записи каждый.

Выполнение этих выборок в только что запущенной базе данных (так что к этому моменту кэширование не требуется!) База данных дает следующие результаты (пропуская полный вывод, обратите внимание, что я даже не прошу базу данных отсортировать результаты):

ВЫБЕРИТЕ post.postid, post.attach ОТ newbb_post AS post ГДЕ post.threadid = 51506;

,
,
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 рядов в наборе (0,13 с)
ВЫБЕРИТЕ post.postid, post.attach ОТ newbb_innopost КАК post, ГДЕ post.threadid = 51506;
,
,
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 рядов в наборе (1 мин 22,19 с)

0,13 секунды до 86,19 секунды (!)

Мне интересно, почему это происходит. Я прочитал некоторые ответы здесь на Stackexchange с участием InnoDB, а некоторые предлагают увеличить innodb_buffer_poolразмер до 80% установленной оперативной памяти. Это не решит проблему, так как первоначальный запрос к определенному идентификатору займет не менее 50 раз и остановит весь веб-сервер, ставя в очередь соединения и запросы к базе данных. После этого может сработать кеш / буфер, но в этой базе данных более 100 000 потоков, поэтому весьма вероятно, что кеш никогда не будет содержать все соответствующие запросы, которые будут обслуживаться.

Вышеприведенные запросы просты (без объединений), и используются все ключи:

ОБЪЯСНИТЬ ВЫБРАТЬ post.postid, post.attach ОТ newbb_innopost КАК post, ГДЕ post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
| id | select_type | стол | тип | возможные_ключи | ключ | key_len | ref | строки | Extra |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +
| 1 | ПРОСТО | пост | ref | threadid, threadid_2, threadid_visible_dateline | нить | 4 | конст | 120144 | |
+ ------ + ------------- + ------- + ------ + ------------- ---------------------------------- + ---------- + ---- ----- + ------- + -------- + ------- +

Это MyISAM-таблица:

CREATE TABLE `newbb_post` (
  `postid` int (10) без знака NOT NULL AUTO_INCREMENT,
  `threadid` int (10) unsigned NOT NULL DEFAULT '0',
  `parentid` int (10) без знака NOT NULL ПО УМОЛЧАНИЮ '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) NOT NULL DEFAULT '0',
  `showignature` smallint (6) NOT NULL DEFAULT '0',
  `ipaddress` varchar (15) NOT NULL DEFAULT '',
  `iconid` smallint (5) без знака NOT NULL DEFAULT '0',
  `visible` smallint (6) NOT NULL DEFAULT '0',
  `attach` smallint (5) unsigned NOT NULL DEFAULT '0',
  `нарушение` smallint (5) без знака NOT NULL ПО УМОЛЧАНИЮ '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') NOT NULL DEFAULT 'on_nl2br',
  ПЕРВИЧНЫЙ КЛЮЧ (`postid`),
  KEY `threadid` (` threadid`, `userid`),
  KEY `importpost_index` (` importpostid`),
  KEY `dateline` (` даталайн`),
  KEY `threadid_2` (` threadid`, `visible`,` dateline`),
  KEY `преобразованный_2_utf8` (` преобразованный_2_utf8`),
  KEY `threadid_visible_dateline` (` threadid`, `visible`,` dateline`, `userid`,` postid`),
  КЛЮЧ `ipaddress` (` ipaddress`),
  КЛЮЧ `userid` (` userid`, `parentid`),
  KEY `user_date` (` userid`, `dateline`)
) ENGINE = MyISAM AUTO_INCREMENT = 5402802 CHARSET ПО УМОЛЧАНИЮ = латиница 1

и это таблица InnoDB (она точно такая же):

CREATE TABLE `newbb_innopost` (
  `postid` int (10) без знака NOT NULL AUTO_INCREMENT,
  `threadid` int (10) unsigned NOT NULL DEFAULT '0',
  `parentid` int (10) без знака NOT NULL ПО УМОЛЧАНИЮ '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) NOT NULL DEFAULT '0',
  `showignature` smallint (6) NOT NULL DEFAULT '0',
  `ipaddress` varchar (15) NOT NULL DEFAULT '',
  `iconid` smallint (5) без знака NOT NULL DEFAULT '0',
  `visible` smallint (6) NOT NULL DEFAULT '0',
  `attach` smallint (5) unsigned NOT NULL DEFAULT '0',
  `нарушение` smallint (5) без знака NOT NULL ПО УМОЛЧАНИЮ '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') NOT NULL DEFAULT 'on_nl2br',
  ПЕРВИЧНЫЙ КЛЮЧ (`postid`),
  KEY `threadid` (` threadid`, `userid`),
  KEY `importpost_index` (` importpostid`),
  KEY `dateline` (` даталайн`),
  KEY `threadid_2` (` threadid`, `visible`,` dateline`),
  KEY `преобразованный_2_utf8` (` преобразованный_2_utf8`),
  KEY `threadid_visible_dateline` (` threadid`, `visible`,` dateline`, `userid`,` postid`),
  КЛЮЧ `ipaddress` (` ipaddress`),
  КЛЮЧ `userid` (` userid`, `parentid`),
  KEY `user_date` (` userid`, `dateline`)
) ENGINE = InnoDB AUTO_INCREMENT = 5402802 CHARSET ПО УМОЛЧАНИЮ = латиница 1

Сервер с 32 ГБ оперативной памяти:

Версия сервера: 10.0.12-MariaDB-1 ~ бинарный дистрибутив trusty-wsrep-log mariadb.org, wsrep_25.10.r4002

Если вам нужна настройка всех переменных innodb_, я могу прикрепить это к этому посту.

Обновить:

Я отбросил ВСЕ индексы отдельно от первичного индекса, после чего результат выглядел так:

,
,
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 рядов в наборе (29,74 с)
ОБЪЯСНИТЬ ВЫБРАТЬ post.postid, post.attach ОТ newbb_innopost КАК post, ГДЕ post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| id | select_type | стол | тип | возможные_ключи | ключ | key_len | ref | строки | Extra |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
| 1 | ПРОСТО | пост | ВСЕ | NULL | NULL | NULL | NULL | 5909836 | Используя где |
+ ------ + ------------- + ------- + ------ + ------------- - + ------ + --------- + ------ + --------- + ------------- +
1 ряд в наборе (0,00 сек)

После этого я просто добавил один индекс обратно в mix, threadid, результаты были следующими:

,
,
| 5402697 | 0 |
| 5402759 | 0 |
+ --------- + -------- +
62510 рядов в наборе (11,58 сек)
ОБЪЯСНИТЬ ВЫБРАТЬ post.postid, post.attach ОТ newbb_innopost КАК post, ГДЕ post.threadid = 51506;
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| id | select_type | стол | тип | возможные_ключи | ключ | key_len | ref | строки | Extra |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
| 1 | ПРОСТО | пост | ref | нить | нить | 4 | конст | 124622 | |
+ ------ + ------------- + ------- + ------ + ------------- - + ---------- + --------- + ------- + -------- + ------- +
1 ряд в наборе (0,00 сек)

Странно, что без каких-либо соответствующих индексов полное сканирование заняло всего 29 секунд по сравнению с 88 секундами с использованием индексов (!).

Имея только один идеально настроенный индекс, он по-прежнему занимает 11 секунд, что слишком медленно для любого реального использования.

Обновление 2:

Я устанавливаю MySQL (5.5.38-0ubuntu0.14.04.1 (Ubuntu)) на другом сервере с точно такой же конфигурацией оборудования и точно такой же базой данных / таблицами.

Результаты практически одинаковы, сначала MyISAM Table:

,
,
| 5401593 | 0 |
| 5401634 | 0 |
+ --------- + -------- +
62510 рядов в наборе (0,14 с)

И это результат таблицы InnoDB

,
,
| 5397410 | 0 |
| 5397883 | 0 |
+ --------- + -------- +
62510 рядов в наборе (1 мин 17,63 сек)

ОБНОВЛЕНИЕ 3: содержимое my.cnf

# Файл конфигурации сервера базы данных MariaDB.
#
# Вы можете скопировать этот файл на один из:
# - "/etc/mysql/my.cnf", чтобы установить глобальные параметры,
# - "~ / .my.cnf", чтобы установить пользовательские параметры.
# 
# Можно использовать все длинные опции, которые поддерживает программа.
# Запустите программу с --help, чтобы получить список доступных опций и с
# --print-defaults, чтобы увидеть, что он на самом деле понимает и использует.
#
# Для объяснения см.
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# Это будет передано всем клиентам MySQL
# Сообщалось, что пароли должны быть заключены в галочки / кавычки
# особенно, если они содержат символы "#" ...
# Не забудьте отредактировать /etc/mysql/debian.cnf при изменении местоположения сокета.
[Клиент]
порт = 3306
socket = /var/run/mysqld/mysqld.sock

# Вот записи для некоторых конкретных программ
# Следующие значения предполагают, что у вас есть как минимум 32M оперативной памяти

# Это было формально известно как [safe_mysqld]. Обе версии в данный момент анализируются.
[Mysqld_safe]
socket = /var/run/mysqld/mysqld.sock
хороший = 0

[ТуздЫ]
#
# * Основные настройки
#
user = mysql
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
порт = 3306
basedir = / usr
datadir = / var / lib / mysql
tmpdir = / tmp
lc_messages_dir = / usr / share / mysql
lc_messages = en_US
пропуск внешнего замок
#
# Вместо пропуска сети по умолчанию теперь слушать только на
# localhost, который более совместим и не менее безопасен.
bind-address = 127.0.0.1
#
# * Тонкая настройка
#
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
#
# Это заменяет скрипт запуска и проверяет таблицы MyISAM при необходимости
# в первый раз они тронуты. В случае ошибки сделайте копию и попробуйте восстановить.
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
#
# * Конфигурация кеша запросов
#
# Кэшировать только крошечные наборы результатов, поэтому мы можем разместить больше в кеше запросов.
query_cache_limit = 128K
query_cache_size = 64M
# для более интенсивных настроек записи, установите на DEMAND или OFF
#query_cache_type = DEMAND
#
# * Ведение журнала и репликация
#
# Обе локации вращаются cronjob.
# Имейте в виду, что этот тип журнала снижает производительность.
# Начиная с 5.1 вы можете включить журнал во время выполнения!
#general_log_file = /var/log/mysql/mysql.log
#general_log = 1
#
# Журнал ошибок идет в системный журнал из-за /etc/mysql/conf.d/mysqld_safe_syslog.cnf.
#
# мы хотим знать о сетевых ошибках и тому подобном
log_warnings = 2
#
# Включить медленный журнал запросов, чтобы увидеть запросы с особенно большой продолжительностью
#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_slow_admin_statements
#
# Следующее может быть использовано как для простого воспроизведения журналов резервного копирования, так и для репликации.
# примечание: если вы настраиваете ведомое устройство репликации, см. README.Debian о
# другие настройки, которые вам может понадобиться изменить.
# 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
# не потрясающе для производительности, но безопаснее
#sync_binlog = 1
expire_logs_days = 10
max_binlog_size = 100M
# рабов
#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
#
# Если приложения поддерживают это, этот более строгий sql_mode предотвращает некоторые
# ошибки, такие как вставка неверных дат и т. д.
#sql_mode = NO_ENGINE_SUBSTITUTION, TRADITIONAL
#
# * InnoDB
#
# InnoDB включен по умолчанию с файлом данных 10 МБ в / var / lib / mysql /.
# Прочтите руководство, чтобы узнать больше об InnoDB. Здесь очень много!
default_storage_engine = InnoDB
# Вы не можете просто изменить размер файла журнала, требуется специальная процедура
#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
#
# * Функции безопасности
#
# Прочтите руководство, если вы хотите chroot!
# chroot = / var / lib / mysql /
#
# Для генерации SSL-сертификатов я рекомендую графический интерфейс OpenSSL "tinyca".
#
# ssl-ca = / etc / mysql / cacert.pem
# ssl-cert = / etc / mysql / server-cert.pem
# ssl-key = / etc / mysql / server-key.pem



[ТуздЫшпр]
быстро
цитата-имена
max_allowed_packet = 16M

[MySQL]
# no-auto-rehash # более быстрый запуск mysql, но не завершение табуляции

[Isamchk]
key_buffer = 16M

#
# * ВАЖНО: Дополнительные настройки, которые могут переопределить те из этого файла!
# Файлы должны заканчиваться на «.cnf», иначе они будут проигнорированы.
#
! includesir /etc/mysql/conf.d/

И содержимое переменных inno:

MariaDB [(none)]> SHOW VARIABLES LIKE 'inno%';
+ ------------------------------------------- + ----- ------------------- +
| Переменное_имя | Значение |
+ ------------------------------------------- + ----- ------------------- +
| 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 | ВЫКЛ |
| innodb_api_enable_binlog | ВЫКЛ |
| innodb_api_enable_mdl | ВЫКЛ |
| innodb_api_trx_level | 0 |
| innodb_autoextend_increment | 64 |
| innodb_autoinc_lock_mode | 1 |
| innodb_buffer_pool_dump_at_shutdown | ВЫКЛ |
| innodb_buffer_pool_dump_now | ВЫКЛ |
| innodb_buffer_pool_filename | ib_buffer_pool |
| innodb_buffer_pool_instances | 8 |
| innodb_buffer_pool_load_abort | ВЫКЛ |
| innodb_buffer_pool_load_at_startup | ВЫКЛ |
| innodb_buffer_pool_load_now | ВЫКЛ |
| innodb_buffer_pool_populate | ВЫКЛ |
| innodb_buffer_pool_size | 21474836480 |
| innodb_change_buffer_max_size | 25 |
| innodb_change_buffering | все |
| innodb_checksum_algorithm | Иннодб |
| innodb_checksums | ON |
| innodb_cleaner_lsn_age_factor | high_checkpoint |
| innodb_cmp_per_index_enabled | ВЫКЛ |
| 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 | утверждать |
| innodb_data_file_path | ibdata1: 12M: автоматическое расширение |
| innodb_data_home_dir | |
| innodb_disable_sort_file_cache | ВЫКЛ |
| innodb_doublewrite | ON |
| innodb_empty_free_list_algorithm | откат |
| innodb_fake_changes | ВЫКЛ |
| innodb_fast_shutdown | 1 |
| innodb_file_format | Антилопа |
| innodb_file_format_check | ON |
| innodb_file_format_max | Антилопа |
| 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 | ВЫКЛ |
| innodb_force_recovery | 0 |
| innodb_foreground_preflush | exponential_backoff |
| innodb_ft_aux_table | |
| innodb_ft_cache_size | 8000000 |
| innodb_ft_enable_diag_print | ВЫКЛ |
| 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 | ВЫКЛ |
| innodb_lock_wait_timeout | 50 |
| innodb_locking_fake_changes | ON |
| innodb_locks_unsafe_for_binlog | ВЫКЛ |
| innodb_log_arch_dir | ./ |
| innodb_log_arch_expire_sec | 0 |
| innodb_log_archive | ВЫКЛ |
| innodb_log_block_size | 512 |
| innodb_log_buffer_size | 8388608 |
| innodb_log_checksum_algorithm | Иннодб |
| 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 | ВЫКЛ |
| innodb_page_size | 16384 |
| innodb_print_all_deadlocks | ВЫКЛ |
| innodb_purge_batch_size | 300 |
| innodb_purge_threads | 1 |
| innodb_random_read_ahead | ВЫКЛ |
| innodb_read_ahead_threshold | 56 |
| innodb_read_io_threads | 4 |
| innodb_read_only | ВЫКЛ |
| innodb_replication_delay | 0 |
| innodb_rollback_on_timeout | ВЫКЛ |
| 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 | ВЫКЛ |
| innodb_stats_persistent | ON |
| innodb_stats_persistent_sample_pages | 20 |
| innodb_stats_sample_pages | 8 |
| innodb_stats_transient_sample_pages | 8 |
| innodb_status_output | ВЫКЛ |
| innodb_status_output_locks | ВЫКЛ |
| innodb_strict_mode | ВЫКЛ |
| 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 | ВЫКЛ |
| innodb_undo_directory | , |
| innodb_undo_logs | 128 |
| innodb_undo_tablespaces | 0 |
| innodb_use_atomic_writes | ВЫКЛ |
| innodb_use_fallocate | ВЫКЛ |
| innodb_use_global_flush_log_at_trx_commit | ON |
| innodb_use_native_aio | ON |
| innodb_use_stacktrace | ВЫКЛ |
| innodb_use_sys_malloc | ON |
| innodb_version | 5.6.17-65.0 |
| innodb_write_io_threads | 4 |
+ ------------------------------------------- + ----- ------------------- +
143 ряда в наборе (0,02 сек)

Количество ядер машины составляет 8, это

Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz по состоянию на /proc/cpuinfo

Последнее замечание: выполнялись запросы с индексами, предложенными RolandoMYSQLDBA, и каждый запрос занимал около 11-20 секунд каждый. Я хочу отметить, что для меня очень важно (это основная таблица доски объявлений), чтобы первый запрос о threadid возвращался менее чем за секунду, так как существует более 60 000 потоков и Google-боты постоянно сканируют эти темы.

Веселый Роджер
источник
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Пол Уайт говорит, GoFundMonica

Ответы:

24

ВАШ ЗАПРОС

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

На первый взгляд этот запрос должен касаться только 1,1597% (62510 из 5390146) таблицы. Это должно быть быстро, учитывая распределение ключей Threadid 51506.

ПРОВЕРКА НА ПРАКТИКЕ

Независимо от того, какую версию MySQL (Oracle, Percona, MariaDB) вы используете, ни одна из них не может сражаться с одним врагом, которого у них всех общего: архитектурой InnoDB.

InnoDB Архитектура

КЛАСТЕРНЫЙ ИНДЕКС

Пожалуйста, имейте в виду, что к каждой записи threadid прикреплен первичный ключ. Это означает, что когда вы читаете из индекса, он должен выполнить поиск первичного ключа в ClusteredIndex (с внутренним именем gen_clust_index) . В ClusteredIndex каждая страница InnoDB содержит как данные, так и информацию индекса PRIMARY KEY. Смотрите мой пост Best of MyISAM и InnoDB для получения дополнительной информации.

ИЗБЫТОЧНЫЕ ИНДЕКСЫ

В таблице много беспорядка, потому что некоторые индексы имеют одинаковые ведущие столбцы. MySQL и InnoDB должны перемещаться между беспорядком индекса, чтобы добраться до нужных узлов BTREE. Вы должны уменьшить этот беспорядок, выполнив следующее:

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

Зачем урезать эти показатели?

  • Первые три индекса начинаются с threadid
  • threadid_2 а также threadid_visible_dateline начать с тех же трех столбцов
  • threadid_visible_dateline не нужно postid, так как это первичный ключ, и он встроен

БУФЕРНОЕ КЕШИНГ

InnoDB Buffer Pool кэширует данные и страницы индекса. MyISAM кэширует только индексные страницы.

Только в этой области MyISAM не тратит время на кеширование данных. Это потому, что он не предназначен для кэширования данных. InnoDB кэширует каждую страницу данных и страницу индекса (и свою бабушку), к которой она обращается. Если ваш буферный пул InnoDB слишком мал, вы можете кэшировать страницы, делать недействительными страницы и удалять страницы в одном запросе.

ТАБЛИЦА ПЛАНОВ

Вы можете сбрить немного места в ряду, учитывая importthreadidи importpostid. У вас есть как BIGINTs. Они занимают 16 байтов в ClusteredIndex на строку.

Вы должны запустить это

SELECT importthreadid,importpostid FROM newbb_innopost PROCEDURE ANALYSE();

Это будет рекомендовать, какие типы данных эти столбцы должны быть для данного набора данных.

ВЫВОД

MyISAM имеет гораздо меньше проблем, чем InnoDB, особенно в области кэширования.

Несмотря на то, что вы раскрыли объем ОЗУ ( 32GB) и версию MySQL ( Server version: 10.0.12-MariaDB-1~trusty-wsrep-log mariadb.org binary distribution, wsrep_25.10.r4002), в этой головоломке есть и другие части, которые вы не раскрыли

  • Настройки InnoDB
  • Количество ядер
  • Другие настройки из my.cnf

Если вы можете добавить эти вещи к вопросу, я могу уточнить.

ОБНОВЛЕНИЕ 2014-08-28 11:27 ПО ВОСТОЧНОМУ ВРЕМЕНИ

Вы должны увеличить потоки

innodb_read_io_threads = 64
innodb_write_io_threads = 16
innodb_log_buffer_size = 256M

Я хотел бы рассмотреть возможность отключения кэша запросов (см. Мой недавний пост, почему query_cache_type отключен по умолчанию, начиная с MySQL 5.6? )

query_cache_size = 0

Я бы сохранил буферный пул

innodb_buffer_pool_dump_at_shutdown=1
innodb_buffer_pool_load_at_startup=1

Увеличьте потоки очистки (если вы используете DML для нескольких таблиц)

innodb_purge_threads = 4

ДАЙТЕ ЭТО ПОПРОБУЙТЕ !!!

RolandoMySQLDBA
источник
Я знаю, что InnoDB должен быть медленнее в тесте с чистой скоростью, но до такой степени? Я читал , что команда MySQL упорно работали , чтобы закрыть этот пробел. Мы все еще имеем дело с ~ 100-кратным увеличением! Вопрос - вы говорите, что запросы такого рода лучше обслуживать с помощью «прямого» некластеризованного индекса B-дерева (т.е. без учета данных PK)? Если так, почему это не было / не было реализовано? Функциональность, которую требует OP, определенно не является предельным вариантом использования.
Verace
Можете ли вы добавить ссылку на полноразмерную версию этой картинки? Некоторые части трудно читать :-)
водянистая
@RolandMySQLDBA спасибо за информацию - надеюсь, вы не предполагаете, что 100-кратное замедление является "нормальным" для InnoDB ... Я мог бы жить с 2x или 3x, но 100x - это слишком много. В ответ на запрос я добавил недостающую информацию в свой вопрос :) Спасибо за пояснения! Количество ядер машины составляет 8.
Jollyroger
2
@watery Вот полный размер фото: scribd.com/doc/31337494/XtraDB-InnoDB-internals-in-drawing
RolandoMySQLDBA
1
Большое спасибо за вашу помощь @RolandoMySQLDBA, к сожалению, даже эти последние изменения не помогли, и InnoDB занимает около 11-20 секунд. Я попробовал кое-что основанное на вашем ответе - отбросить все индексы и создать индекс покрытия. Это очень помогло. Без вашего объяснения индексов я бы не нашел это решение. Собираюсь проверить свой ответ и написать ответ сам, объясняя, что я сделал :)
jollyroger
7

@RolandMySQLDBA дал правильную подсказку, чтобы ответить на вопрос. Проблема, похоже, заключается в запросе, и для того, чтобы результаты были возвращены, каждое из этих полей должно быть прочитано (каким-то образом из базы данных).

Я удалил все индексы, кроме PRIMARY KEY, и вставил этот новый индекс:

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

Эта ссылка объясняет, что здесь происходит ( охватывающий индекс ): Запрашиваемые поля запроса, которые postid,attachтеперь можно извлечь из самого ключа. Это сохраняет проверку реальных данных и использование ввода / вывода на жесткий диск.

Все запросы теперь выполняются с 0,00 секунд .. :)

Большое спасибо всем за вашу помощь.

Изменить : фактическая основная проблема не решена, я просто обошел ее с помощью этой техники. InnoDB нуждается в серьезном исправлении в этой области.

Веселый Роджер
источник
Я сталкиваюсь с той же проблемой. Myisma запрос занимает 0,01 секунды, в то время как innodb занимает 60 секунд, попробую ваши предложения.
AMB
@AMB - 0.01s пахнет как кеш запросов; время это снова с SQL_NO_CACHE.
Рик Джеймс
0

Судя по вашим запросам и таблицам, кажется, что вы выбираете данные из таблицы временных рядов. Таким образом, может быть, что время запроса медленное, потому что вы вставляете одновременно?

Если эти две вещи верны, то могу ли я предложить рассмотреть ScaleDB в качестве альтернативы? Вы все еще будете на MariaDB, просто (возможно) более подходящий движок.

http://www.scaledb.com - домашняя страница http://www.scaledb.com/download-form.php - наш продукт

OShadmon
источник
2
Вы должны добавить, что основное издание не является бесплатным.
ypercubeᵀᴹ
0

Оба движка будут выполнять запрос намного быстрее с

INDEX(threadid, attach, postid)

Это потому, что это будет «покрывающий» индекс, и он будет работать практически так же (используя индекс BTree).

Также я скажу, что это невозможно для любого двигателя на «холодном» сервере:

62510 rows in set (0.13 sec)

Пожалуйста, используйте SQL_NO_CACHE каждый раз при запуске таймингов - мы не хотим, чтобы кеш запросов искажал выводы.

Другой быстрый подход (вне зависимости от кэширования ввода / вывода):

Используйте InnoDB и измените с PRIMARY KEY (postid)на

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

Причина в том, что это приведет к тому, что все соответствующие строки будут смежными, что потребует меньшего количества операций ввода-вывода и т. Д. Это INDEX(postid)должно оставаться AUTO_INCREMENTсчастливым. Предостережение: это портит все второстепенные ключи - некоторые будут быстрее, некоторые медленнее.

Рик Джеймс
источник
0

Хотя это не относится напрямую к @jollyroger, потому что он уже имеет правильные настройки, но я получил значительное улучшение, изменив innodb_buffer_pool_sizeдо 70% своей оперативной памяти, как объяснено в разделе Почему myisam медленнее, чем Innodb

Сначала MyISAMбыло медленно, но хорошо. Затем все InnoDBстало плохо, как в 100 раз медленнее в этом вопросе, и после изменения настройки InnoDBстал в 10 раз быстрее MyISAM.

Моя настройка по умолчанию была на 8 МБ, что очень мало.

Хьюго Делсинг
источник