2015-12-27 3 views
4

Мое серверное приложение делает несколько подключений к MySQL через отдельные потоки. Каждое соединение вызывает запрос SELECT и извлекает результат, который приложение затем возвращает к подключенным пользователям.MySQL: выбор выполнения запроса и увеличение времени выборки результатов с количеством подключений

Я пользуюсь InnoDB. К моему удивлению, мне показалось очень странным, что, если я увеличиваю количество подключений к MySQL, производительность запросов ухудшится, и время выборки также увеличится. Ниже приведена таблица.

Эти данные создаются, когда у меня было 3333 записей в таблице MySQL, а запрос SELECT, основанный на случайных параметрах, предоставленных ему, извлекает около 450 записей из них. Каждая запись содержит около 10 полей, и все они вместе содержат 1,2 КБ данных. (Таким образом, единый SELECT запрос получает 1,2 * 450 = 540 Кбайт данных в общей сложности)

 
╔═══════════╦═══════════════╦══════════════╗ 
║ Number of ║Query execution║ Result fetch ║ 
║connections║ time range ║ time range ║ 
║ to MySQL ║ (in seconds) ║ (in seconds) ║ 
╠═══════════╬═══════════════╬══════════════╣ 
║  1  ║ 0.02 to 0.06 ║ 0.03 to 0.18 ║ 
║  7  ║ 0.23 to 0.64 ║ 0.54 to 0.74 ║ 
║ 17  ║ 0.32 to 1.71 ║ 0.53 to 1.18 ║ 
║ 37  ║ 0.37 to 2.01 ║ 0.70 to 1.70 ║ 
║ 117  ║ 1.13 to 3.29 ║ 2.48 to 3.25 ║ 
╚═══════════╩═══════════════╩══════════════╝ 

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

Я не буду возражать, чтобы иметь одно соединение с БД, но поймать, что производительность моего сервера значительно ухудшается. Тысячи пользователей (подключенных к моему серверу) должны будут ждать, пока этот единственный поток не появится.

Пройдя некоторые связанные с этим вопросы, я попытался увеличить innodb_buffer_pool_size до 1 GB без везения.

Вот мои все InnoDB параметры:

innodb_adaptive_flushing ON 
innodb_adaptive_flushing_lwm 10 
innodb_adaptive_hash_index ON 
innodb_adaptive_max_sleep_delay 150000 
innodb_additional_mem_pool_size 2097152 
innodb_api_bk_commit_interval 5 
innodb_api_disable_rowlock OFF 
innodb_api_enable_binlog OFF 
innodb_api_enable_mdl OFF 
innodb_api_trx_level 0 
innodb_autoextend_increment 64 
innodb_autoinc_lock_mode 1 
innodb_buffer_pool_dump_at_shutdown OFF 
innodb_buffer_pool_dump_now OFF 
innodb_buffer_pool_filename ib_buffer_pool 
innodb_buffer_pool_instances 8 
innodb_buffer_pool_load_abort OFF 
innodb_buffer_pool_load_at_startup OFF 
innodb_buffer_pool_load_now OFF 
innodb_buffer_pool_size 1073741824 
innodb_change_buffer_max_size 25 
innodb_change_buffering all 
innodb_checksum_algorithm crc32 
innodb_checksums ON 
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_data_file_path ibdata1:12M:autoextend 
innodb_data_home_dir  
innodb_disable_sort_file_cache OFF 
innodb_doublewrite ON 
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 2 
innodb_flush_method normal 
innodb_flush_neighbors 1 
innodb_flushing_avg_loops 30 
innodb_force_load_corrupted OFF 
innodb_force_recovery 0 
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 200 
innodb_io_capacity_max 2000 
innodb_large_prefix OFF 
innodb_lock_wait_timeout 50 
innodb_locks_unsafe_for_binlog OFF 
innodb_log_buffer_size 268435456 
innodb_log_compressed_pages ON 
innodb_log_file_size 262144000 
innodb_log_files_in_group 2 
innodb_log_group_home_dir .\ 
innodb_lru_scan_depth 1024 
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 300 
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 64 
innodb_read_only OFF 
innodb_replication_delay 0 
innodb_rollback_on_timeout OFF 
innodb_rollback_segments 128 
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 8 
innodb_thread_sleep_delay 0 
innodb_undo_directory . 
innodb_undo_logs 128 
innodb_undo_tablespaces 0 
innodb_use_native_aio OFF 
innodb_use_sys_malloc ON 
innodb_version 5.6.28 
innodb_write_io_threads 16 

Может кто-то пожалуйста, проливают свет? Меня это раздражает очень долго.

(Примечание: я не упомянул фактический запрос в этом вопросе, потому что запрос немного сложнее, и этот вопрос не о том, что запрос Но речь идет о деградации производительности с увеличением соединений при запросе такой же.)

UPDATE 1

Вот SHOW CREATE TABLE выход для моих таблиц:

CREATE TABLE `profiles` (
    `SRNO` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
    `HANDLE_FIRST` int(10) unsigned NOT NULL, 
    `HANDLE_SECOND` bigint(20) unsigned NOT NULL, 
    `USERID` binary(16) NOT NULL, 
    `UNIQUESTRING` char(10) NOT NULL, 
    `CLIENT_VERSION` smallint(5) unsigned NOT NULL, 
    `ISCONNECTED` bit(1) NOT NULL, 
    `ISPROFILEPRESENT` bit(1) NOT NULL, 
    `USERNAME` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, 
    `GENDER` tinyint(1) DEFAULT NULL, 
    `DND` bit(1) DEFAULT NULL, 
    `STATUS` varchar(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, 
    `PROFILE_URL` varchar(128) DEFAULT NULL, 
    PRIMARY KEY (`SRNO`), 
    UNIQUE KEY `USERID` (`USERID`), 
    KEY `USERID_INDEX` (`USERID`), 
    KEY `UNIQUESTRING_INDEX` (`UNIQUESTRING`), 
    KEY `ISCONNECTED_INDEX` (`ISCONNECTED`), 
    KEY `ISPROFILEPRESENT_INDEX` (`ISPROFILEPRESENT`) 
) ENGINE=InnoDB AUTO_INCREMENT=9250 DEFAULT CHARSET=utf8 


CREATE TABLE `blockers` (
    `BLOCKER_PROFILE_SRNO` bigint(20) unsigned NOT NULL, 
    `BLOCKED_PROFILE_SRNO` bigint(20) unsigned NOT NULL, 
    UNIQUE KEY `BLOCKER_PROFILE_SRNO` (`BLOCKER_PROFILE_SRNO`,`BLOCKED_PROFILE_SRNO`), 
    KEY `BLOCKER_PROFILE_SRNO_INDEX` (`BLOCKER_PROFILE_SRNO`), 
    KEY `BLOCKED_PROFILE_SRNO_INDEX` (`BLOCKED_PROFILE_SRNO`), 
    CONSTRAINT `fk_BlockedIndex` FOREIGN KEY (`BLOCKED_PROFILE_SRNO`) REFERENCES `profiles` (`SRNO`), 
    CONSTRAINT `fk_BlockerIndex` FOREIGN KEY (`BLOCKER_PROFILE_SRNO`) REFERENCES `profiles` (`SRNO`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

И вот запрос я бегу:

select prfls.* 
    from profiles as prfls 
    left outer join blockers as blkr1 on blkr1.blocker_profile_srno = prfls.srno 
     and blkr1.blocked_profile_srno = 6443 
    left outer join blockers as blkr2 on blkr2.blocker_profile_srno = 6443 
     and blkr2.blocked_profile_srno = prfls.srno 
    where blkr1.blocker_profile_srno is null 
     and blkr2.blocker_profile_srno is null 
     and (  (prfls.uniquestring like 'phk5600dcc%') 
       or (prfls.uniquestring like 'phk5600dcf%') 
      ) 
     and prfls.isconnected=1 
     and prfls.isprofilepresent=1 
    limit 450 

Этот запрос по существу подготовленное заявление, где blocked_profile_srno, blocker_profile_srno и uniquestring параметры постоянно меняются для каждого запроса. Однако blocked_profile_srno и blocker_profile_srno всегда остаются равными (в приведенном выше запросе их значение равно 6443). Таблица blockers пуста (я использую ее для использования в будущем, но в настоящее время она не имеет данных)

Когда 117 соединений выполнялись одновременно с запросами, выход SHOW GLOBAL STATUS LIKE 'Threads_running'; был в большинстве случаев 1. Однако иногда он поднимался до 27 ,В то же время, выход SHOW GLOBAL STATUS LIKE 'Max_used_connections'; был 130

UPDATE 2

я могу собрать из Rick James ответа ниже, оптимизирующий запрос уменьшает запрос диапазон времени выполнения. Этот временной диапазон продолжает увеличиваться с количеством подключений, но в пределах допустимого диапазона. Вот почему я принял ответ.

+0

Общее количество выполненных запросов в секунду отличается для различного количества соединений? Сколько ядер вашего процессора? –

+0

Какое оборудование вы используете? –

+0

На самом деле это не высокотехнологичное оборудование. Двойной ядро ​​[email protected] ГГц с 4 ГБ ОЗУ и ОС - Windows 8. – Atul

ответ

2

Возможно, каждое соединение выполняет полную проверку таблицы profiles. Давайте попробуем это избежать. Когда десятки запросов попадают в одну и ту же таблицу, есть блокировки, которые заставляют InnoDB «наткнуться на себя». Любой из этих планов ускорит запрос и уменьшит количество затронутых строк (следовательно, уменьшит блокировку). Использование предлагаемого «составного» индекса ускорит запрос. Но OR мешает. Я вижу два трюка, которые все еще имеют индексный взгляд на uniquestring, но избегайте некоторых или всех OR.

(  (prfls.uniquestring like 'phk5600dcc%') 
    or (prfls.uniquestring like 'phk5600dcf%') 
) 

OR сложно оптимизировать.

Добавить это:

INDEX(isconnected, isprofilepresent, uniquestring) 

Тогда ...

Plan A:

prfls.uniquestring   like 'phk5600dc%' AND -- note common prefix 
(  (prfls.uniquestring like 'phk5600dcc%') 
    or (prfls.uniquestring like 'phk5600dcf%') 
) 

Это предполагает, что вы можете построить этот общий префикс.

Plan B (поворот OR в UNION):

(SELECT ... 
    WHERE prfls.uniquestring like 'phk5600dcc%' AND ... 
    LIMIT 450) 
UNION ALL -- ? You may want DISTINCT, if there could be dups 
(SELECT ... 
    WHERE prfls.uniquestring like 'phk5600dcf%' AND ... -- the only diff 
    LIMIT 450) 
LIMIT 450 -- yes, again 

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

Другие ноты ...

Индексы на флагах (из которых у вас есть два) почти никогда не используется. EXPLAIN SELECT ..., вероятно, покажет, что ни один из них не использовался. Пожалуйста, предоставьте EXPLAIN для любого SELECT, который нуждается в обсуждении.

A UNIQUE KEY - KEY, поэтому нет необходимости в избыточном индексе на USERID.

limit 450 - Какой 450 вы хотите? Без ORDER BY, запрос разрешен, чтобы дать вам любой 450. (Конечно, возможно, что это хорошо.) (А ORDER BY, вероятно, замедлить запрос.)

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

+0

Спасибо за эти ценные входы. Я буду оптимизировать запрос. Но я просто попытался выполнить простой запрос 'SELECT * FROM PROFILES WHERE SRNO>? LIMIT 450' (с различным «SRNO» от каждого соединения), и это также показывает то же поведение (т. Е. С увеличением количества запросов на соединение требуется больше времени для запуска). Теперь этот запрос также выполнил бы полное сканирование таблицы, что привело бы к блокировкам? Я фактически выбрал InnoDB, потому что я читал, что он лучше всего подходит для параллельной обработки запросов, поскольку он имеет возможность блокировать уровень блокировки в отличие от полных блокировок таблицы в MyISAM. И я не указываю конкретно блокировку, почему InnoDB блокирует таблицу? – Atul

+0

Хорошо, это лучший тестовый случай для преследования. Вопросы ... Какова настройка автообмена? Является ли 'SELECT' частью' BEGIN ... COMMIT'? Что говорит 'SHOW ENGON INNODB STATUS' во время такого замедления? –

+0

автокоммит не был. Нет особого времени, когда все соединения будут замедляться. Я выполнил упражнение. Запущено 1000 запросов SELECT на 100 подключений одновременно (по 10 на каждый). Некоторые запросы заняли 0,002 секунды, а некоторые заняли 0,2 секунды. Между этой яростью остался отдых. Нет такой модели, как я нашел. Это почти случайно. Вместо 100 соединений, если бы у меня было 10 соединений, этот диапазон составлял бы от 0,002 секунды до 0,02 сек. ** Диапазон несогласованности увеличивается с количеством подключений ** – Atul