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