簡體   English   中英

帶有 ORDER BY 的 MySQL 查詢需要很長時間才能執行

[英]MySQL query with ORDER BY takes long time to execute

我有一個名為“response_set”的表,其中包含以下索引(“show create table response_set;”的結果):

| response_set | CREATE TABLE `response_set` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `survey_id` int(11) NOT NULL DEFAULT '0',
  `respondent_id` int(11) DEFAULT NULL,
  `ext_ref` varchar(64) DEFAULT NULL,
  `email_addr` varchar(128) DEFAULT NULL,
  `ip` varchar(32) DEFAULT NULL,
  `t` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `time_taken` int(11) DEFAULT NULL,
  `category_id` int(11) DEFAULT NULL,
  `duplicate` int(1) DEFAULT '0',
  `email_group` varchar(30) DEFAULT NULL,
  `external_email_id` int(11) DEFAULT NULL,
  `geo_code_country` varchar(64) DEFAULT NULL,
  `geo_code_country_code` varchar(2) DEFAULT NULL,
  `terminated_survey` int(1) DEFAULT NULL,
  `geo_code_region` varchar(128) DEFAULT NULL,
  `geo_code_city` varchar(3) DEFAULT NULL,
  `geo_code_area_code` varchar(3) DEFAULT NULL,
  `geo_code_dma_code` varchar(3) DEFAULT NULL,
  `restart_url` varchar(255) DEFAULT NULL,
  `inset_list` varchar(1024) DEFAULT NULL,
  `custom1` varchar(1024) DEFAULT NULL,
  `custom2` varchar(1024) DEFAULT NULL,
  `custom3` varchar(1024) DEFAULT NULL,
  `custom4` varchar(1024) DEFAULT NULL,
  `panel_member_id` int(11) DEFAULT NULL,
  `external_id` int(11) DEFAULT NULL,
  `weight` float DEFAULT NULL,
  `custom5` varchar(1024) DEFAULT NULL,
  `quota_overlimit` int(1) DEFAULT '0',
  `panel_id` int(11) DEFAULT NULL,
  `referer_url` varchar(255) DEFAULT NULL,
  `referer_domain` varchar(64) DEFAULT NULL,
  `user_agent` varchar(255) DEFAULT NULL,
  `longitude` decimal(15,12) DEFAULT '0.000000000000',
  `latitude` decimal(15,12) DEFAULT '0.000000000000',
  `radius` decimal(7,2) DEFAULT '0.00',
  `cx_business_unit_id` int(11) DEFAULT '0',
  `survey_link_id` int(11) DEFAULT '0',
  `data_quality_flag` int(1) DEFAULT '0',
  `data_quality_score` double DEFAULT '0',
  `extended_info_json` json DEFAULT NULL,
  `updated_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `channel` int(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `panel_member_id` (`panel_member_id`),
  KEY `panel_member_id_2` (`panel_member_id`),
  KEY `email_group` (`email_group`),
  KEY `email_group_2` (`email_group`),
  KEY `survey_timestamp_idx` (`survey_id`,`t`),
  KEY `cx_business_unit_id_idx` (`cx_business_unit_id`),
  KEY `data_quality_flag_idx` (`data_quality_flag`),
  KEY `data_quality_score_idx` (`data_quality_score`),
  KEY `survey_timestamp_terminated_idx` (`survey_id`,`t`,`terminated_survey`),
  KEY `survey_idx` (`survey_id`)
) ENGINE=InnoDB AUTO_INCREMENT=39759 DEFAULT CHARSET=utf8 |

現在,我在頁面上執行以下查詢,以檢索基於survey_id 和order by id 的response_set 行:

SELECT * 
FROM response_set a 
WHERE a.survey_id = 1602673827 
ORDER BY a.id limit 100;

問題是有時查詢執行時間超過 30 秒,並且此行為不一致(因為有時按 a.id 排序,有時按 a.id DESC 排序時會發生這種情況,因為用戶可以按升序查看響應集或頁面上的降序)不同的survey_id。

表中有大約 620 萬條記錄,對於給定的survey_id (1602673827),有 45,800 條記錄。 在使用 EXPLAIN SELECT 語句來理解查詢執行計划時,我得到了以下信息:

+----+-------------+-------+------------+-------+------------------------------------------------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys                                        | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+------------------------------------------------------+---------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | a     | NULL       | index | survey_timestamp_idx,survey_timestamp_terminated_idx | PRIMARY | 4       | NULL | 6863 |     1.46 | Using where |
+----+-------------+-------+------------+-------+------------------------------------------------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

現在我無法理解,即使存在索引 -> 'survey_timestamp_idx,survey_timestamp_terminated_idx' ,為什么 MySQL 不使用索引並選擇全表掃描。 此外,當我修改查詢如下:

SELECT * 
FROM response_set a USE INDEX (survey_timestamp_idx) 
WHERE a.survey_id = 1602673827 
ORDER BY a.id  limit 100;

查詢執行時間減少到 0.17 秒。 在為修改后的查詢做 EXPLAIN 時,我得到以下信息:

+----+-------------+-------+------------+------+----------------------+----------------------+---------+-------+-------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys        | key                  | key_len | ref   | rows  | filtered | Extra                                 |
+----+-------------+-------+------------+------+----------------------+----------------------+---------+-------+-------+----------+---------------------------------------+
|  1 | SIMPLE      | a     | NULL       | ref  | survey_timestamp_idx | survey_timestamp_idx | 4       | const | 87790 |   100.00 | Using index condition; Using filesort |
+----+-------------+-------+------------+------+----------------------+----------------------+---------+-------+-------+----------+---------------------------------------+
1 row in set, 1 warning (0.00 sec)

但是,我不想在查詢中顯式使用“USE INDEX”,因為 where 子句是動態的,並且根據用戶選擇的過濾器可能在 where 子句中包含以下組合:

1. where survey_id = ?;
2. where survey_id = ? and t = ?; (t is timestamp)
3. where survey_id = ? and terminated_survey = ?;
4. where survey_id = ? and t = ? and terminated_survey = ?;

此外,如果我從查詢中刪除 ORDER BY 子句,則查詢始終使用索引並且執行得非常快。

有沒有其他方法可以讓 MySQL 查詢引擎在查詢中存在 ORDER BY 子句時選擇正確(更快)的執行計划(通過使用正確的索引)?

我使用的是MySQL 版本:5.7.22

我已經閱讀了 ORDER BY 查詢優化的 MySQL 官方文檔 ( https://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html ) 並嘗試在 (id,survey_id) 上添加復合索引和 (survey_id, id) 但它沒有用。 有人可以幫忙嗎?

  1. 其中survey_id = ?;
  2. 其中survey_id = ? 和 t = ?; (t 是時間戳)
  3. 其中survey_id = ? 並已終止調查 = ?;
  4. 其中survey_id = ? 和 t = ? 並已終止調查 = ?;

假設您有ORDER BY id ASC (or DESC) ,那么您需要 4 個索引來以最佳方式處理所有這些索引。 WHERE提到的 1、2 或 3 列(以任何順序)開始,然后以id結束。

我無法解釋為什么 KEY survey_idx ( survey_id ) 沒有用於相關查詢,該索引也不是EXPLAIN的“possible_key”。 就好像在運行查詢和發布這個問題之間發生了一些變化。 請重新檢查。

順便說一句, INT(1)仍然需要 4 個字節; 你可能想要一字節的TINYINT UNSIGNED 許多其他領域都比必要的要大。 尺寸影響性能,至少有一點。

0.17s -- 使用FORCE INDEX(survey_idx)可能會更快

PRIMARY KEY (如(id, survey_id) )幾乎總是沒用的。 索引應該開始使用=測試的內容,然后移動到作為范圍或GROUP BY或(如您的情況), ORDER BY

食譜: http : //mysql.rjweb.org/doc.php/index_cookbook_mysql

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM