[英]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) 但它沒有用。 有人可以幫忙嗎?
假設您有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
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.