【问题标题】:MySQL: Select query execution and result fetch time increases with number of connectionsMySQL:选择查询执行和结果获取时间随着连接数的增加而增加
【发布时间】:2015-12-27 20:13:05
【问题描述】:

我的服务器应用程序通过单独的线程与 MySQL 建立多个连接。每个连接都会触发一个SELECT 查询并获取结果,然后应用程序将这些结果返回给连接的用户。

我正在使用 InnoDB。令我惊讶的是,如果我增加与 MySQL 的连接数,查询性能会下降并且结果获取时间也会增加,这很奇怪。下面是一个显示相同的表格。

当我在 MySQL 表中有 3333 条记录并且基于给定随机参数的 SELECT 查询从中获取大约 450 条记录时,会生成此数据。每条记录大约有 10 个字段,所有字段加起来包含 1.2 KB 的数据。 (因此,单个 SELECT 查询总共获取 1.2 * 450 = 540 KB 数据)

╔═══════════╦═══════════════╦════════════╕ ║查询执行次数║结果获取║ ║连接║时间范围║时间范围║ ║ 到 MySQL ║(以秒为单位)║(以秒为单位)║ ╠═══════════╬═══════════════╬═════════════ ║ 1 ║ 0.02 到 0.06 ║ 0.03 到 0.18 ║ ║ 7 ║ 0.23 至 0.64 ║ 0.54 至 0.74 ║ ║ 17 ║ 0.32 至 1.71 ║ 0.53 至 1.18 ║ ║ 37 ║ 0.37 至 2.01 ║ 0.70 至 1.70 ║ ║ 117 ║ 1.13 至 3.29 ║ 2.48 至 3.25 ║ ╚═══════════╩═══════════════╩════════════╕

我不明白的是,为什么 MySQL 在连接数量增加时会花费更多时间?特别是当没有对表进行更新时,MySQL 应该在单独的线程中处理来自每个连接的SELECT 请求。从而并发处理查询。因此,理想情况下,性能和获取不应有显着下降。

我不介意与数据库建立单一连接,但要注意的是,我的服务器性能会因此而显着降低。成千上万的用户(连接到我的服务器)将不得不等待该单个线程轮到他们。

在解决了一些关于 SO 的相关问题后,我尝试增加 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

有人可以投光吗?这个问题困扰了我很久。

注意:我没有在这个问题中提到实际的查询,因为查询有点复杂,这个问题与那个查询无关。但是当查询相同时,随着连接的增加,性能下降)

更新 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_srnoblocker_profile_srnouniquestring 参数针对每个查询不断变化。但是 blocked_profile_srnoblocker_profile_srno 始终保持相等(在上面的查询中,它们的值为 6443)。表 blockers 是空白的(我已经准备好以备将来使用,但目前它没有数据)

当 117 个连接同时运行查询时,SHOW GLOBAL STATUS LIKE 'Threads_running'; 的输出大部分时间为 1。但有时会上升到 27。同时,SHOW GLOBAL STATUS LIKE 'Max_used_connections'; 的输出为 130

更新 2

我可以从 Rick James 下面的回答中得知,优化查询会减少查询执行时间范围。这个时间范围仍然随着连接数的增加而不断增加,但在可接受的范围内。这就是我接受答案的原因。

【问题讨论】:

  • 不同连接数每秒执行的查询总数不同?你的 cpu 有多少个核心?
  • 你在什么硬件上运行这个?
  • 嗯,它确实不是高端硬件。双核 CPU@3.00 GHz,4 GB RAM,操作系统为 Windows 8。
  • 请提供SHOW CREATE TABLE 和样品SELECT。通常索引和查询决定性能。
  • 你说有 117 个连接,但有 117 个查询同时运行吗?在 117 运行时向我们展示 SHOW GLOBAL STATUS LIKE 'Threads_running';SHOW GLOBAL STATUS LIKE 'Max_used_connections';

标签: mysql innodb database-performance query-performance


【解决方案1】:

可能每个连接都在对profiles 进行全表扫描。让我们尽量避免这种情况。当有几十个查询访问同一个表时,会有一些锁导致 InnoDB “绊倒自己”。这些计划中的任何一个都将加快查询速度并减少接触的行数(因此减少锁定)。使用建议的“复合”索引将加快查询速度。但是OR 妨碍了。我看到两个技巧仍然可以查看uniquestring 的索引,但要避免部分或全部OR

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

OR 很难优化。

添加这个:

INDEX(isconnected, isprofilepresent, uniquestring)

那么……

A 计划:

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

这假设您可以构造该公共前缀。

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

A 计划(如果可行)利用看似的共同起始值。计划 B 无论如何都行得通,但可能会慢一点,尽管仍然比原来的快很多。

其他注意事项...

标志上的索引(你有两个)几乎从不使用。 EXPLAIN SELECT ... 可能会表明两者都没有被使用。请为任何需要讨论的SELECT 提供EXPLAIN

UNIQUE KEYKEY,因此不需要USERID 上的冗余索引。

limit 450 -- 你想要哪个 450?如果没有ORDER BY,则允许查询为您提供任何 450。(当然,也许这很好。)(而ORDER BY 可能会减慢查询速度。)

我的建议不会“解决”问题,但它们应该在减速变得明显之前增加连接数。

【讨论】:

  • 感谢您的宝贵意见。我将优化查询。但是我只是尝试运行简单的查询SELECT * FROM PROFILES WHERE SRNO > ? LIMIT 450(每个连接使用不同的SRNO),这也显示出相同的行为(即随着连接数量的增加,查询需要更长的时间才能运行)。现在,这个查询也会做全表扫描导致锁吗?事实上,我之所以选择 InnoDB,是因为我读到它最适合并发查询处理,因为它具有行级锁定的能力,这与 MyISAM 中的全表锁定不同。而且我没有明确指定锁为什么 InnoDB 锁表?
  • 好的,这是一个更好的测试用例。问题...自动提交的设置是什么? SELECTBEGIN...COMMIT 的一部分吗? SHOW ENGINE INNODB STATUS 在这种放缓的情况下说了什么?
  • 自动提交已关闭。没有特定时间所有连接都会变慢。我做了一个练习。同时通过 100 个连接触发 1000 个 SELECT 查询(因此每个 10 个),一些查询花费了 0.002 秒,而一些查询花费了 0.2 秒。休息在这种愤怒之间。我没有找到这样的模式。这几乎是随机的。如果我有 10 个连接,而不是 100 个连接,那么这个范围将是 0.002 秒到 0.02 秒。 不一致的范围随着连接数的增加而扩大
  • 当我进行这个练习时,在SHOW ENGINE INNODB STATUS TRANSACTIONS 下有重复行“MySQL 线程 id 5358,OS 线程句柄 0xdd0,查询 id 584867 localhost 127.0.0.1 dbuser 清理”和 FILE I/O 还有重复行“I/O 线程 2 状态:等待 i/o 请求(读取线程)”和“I/O 线程 81 状态:等待 i/o 请求(写入线程)”除此之外不能'没有看到任何警告或错误。
  • 那么,所有 10 个 SELECTs 都在 same 事务中?我认为这是不现实的。打开自动提交。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多