У меня довольно неприятная проблема. Я хочу использовать 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-боты постоянно сканируют эти темы.
