簡體   English   中英

內部連接順序查詢緩慢

[英]Slow inner join order query

我的查詢速度有問題。 問題與此類似,但找不到解決方案。 解釋說MySQL正在使用: Using index condition; Using where; Using temporary; Using filesort Using index condition; Using where; Using temporary; Using filesort 在公司桌上Using index condition; Using where; Using temporary; Using filesort

MySQL慢速查詢:INNER JOIN + ORDER BY導致文件排序

查詢速度慢:

SELECT * FROM companies 
    INNER JOIN post_indices
        ON companies.post_index_id = post_indices.id
    WHERE companies.deleted_at is NULL
    ORDER BY post_indices.id
    LIMIT 1;
# 1 row in set (5.62 sec)

但是,如果我從查詢中刪除where語句,那確實非常快:

SELECT * FROM companies
    INNER JOIN post_indices
        ON companies.post_index_id = post_indices.id
    ORDER BY post_indices.id
    LIMIT 1;
# 1 row in set (0.00 sec)

我試過在companies表上使用不同的索引:

  1. index_companies_on_deleted_at
  2. index_companeis_on_post_index_id
  3. index_companies_on_deleted_at_and_post_index_id
  4. index_companies_on_post_index_id_and_deleted_at

MySQL會自動選擇index_companies_on_deleted_at索引。 使用上述索引的同一查詢的統計信息:

  1. 5.6秒
  2. 3.4秒
  3. 8.5秒
  4. 3.5秒

有什么想法可以提高我的查詢速度嗎? 再次說-沒有where deleted_at is null情況,條件查詢是即時的。

  • 公司表有130萬行。
  • PostIndices表有3k行。

更新1:

由於已建立索引,因此使用post_indices.id訂購是為了簡化。 但是它將用於post_indices表的其他列( post_indices )。 因此,對companies.post_index_id排序將無法解決此問題

更新2:為里克·詹姆斯

您的查詢僅需0.04秒即可完成。 並解釋說使用index_companies_on_deleted_at_and_post_index_id索引。 所以是的,它工作得更好,但是並不能解決我的問題(需要在post_indices列上進行排序,將來會這樣做,因此id post_indices.id用於簡化示例。以后將是例如post_indices.city )。

我的查詢在WHERE,但沒有ORDER BY是即時的。

更新3:

EXPLAIN查詢。 我也注意到索引的順序很重要。 index_companies_on_deleted_at指數如果是更高的(前面創建的),則使用index_companies_on_deleted_at_and_post_index_id 否則,將使用更高版本的索引。 我的意思是MySQL自動選擇的。

mysql> EXPLAIN SELECT * FROM companies INNER JOIN post_indices ON post_indices.id = companies.post_index_id WHERE companies.deleted_at IS NULL ORDER BY post_indices.id LIMIT 1;
+----+-------------+--------------+------------+--------+----------------------------------------------------------------------------------------------------------------+-------------------------------+---------+------------------------------------------------------+--------+----------+---------------------------------------------------------------------+
| id | select_type | table        | partitions | type   | possible_keys                                                                                                  | key                           | key_len | ref                                                  | rows   | filtered | Extra                                                               |
+----+-------------+--------------+------------+--------+----------------------------------------------------------------------------------------------------------------+-------------------------------+---------+------------------------------------------------------+--------+----------+---------------------------------------------------------------------+
|  1 | SIMPLE      | companies    | NULL       | ref    | index_companies_on_post_index_id,index_companies_on_deleted_at,index_companies_on_deleted_at_and_post_index_id | index_companies_on_deleted_at | 6       | const                                                | 638692 |   100.00 | Using index condition; Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | post_indices | NULL       | eq_ref | PRIMARY                                                                                                        | PRIMARY                       | 4       | enbro_purecrm_eu_development.companies.post_index_id |      1 |   100.00 | NULL                                                                |
+----+-------------+--------------+------------+--------+----------------------------------------------------------------------------------------------------------------+-------------------------------+---------+------------------------------------------------------+--------+----------+---------------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)


mysql> EXPLAIN SELECT * FROM companies USE INDEX(index_companies_on_post_index_id) INNER JOIN post_indices ON post_indices.id = companies.post_index_id WHERE companies.deleted_at IS NULL ORDER BY post_indices.id LIMIT 1;
+----+-------------+--------------+------------+--------+----------------------------------+---------+---------+------------------------------------------------------+---------+----------+----------------------------------------------+
| id | select_type | table        | partitions | type   | possible_keys                    | key     | key_len | ref                                                  | rows    | filtered | Extra                                        |
+----+-------------+--------------+------------+--------+----------------------------------+---------+---------+------------------------------------------------------+---------+----------+----------------------------------------------+
|  1 | SIMPLE      | companies    | NULL       | ALL    | index_companies_on_post_index_id | NULL    | NULL    | NULL                                                 | 1277385 |    10.00 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | post_indices | NULL       | eq_ref | PRIMARY                          | PRIMARY | 4       | enbro_purecrm_eu_development.companies.post_index_id |       1 |   100.00 | NULL                                         |
+----+-------------+--------------+------------+--------+----------------------------------+---------+---------+------------------------------------------------------+---------+----------+----------------------------------------------+
2 rows in set, 1 warning (0.00 sec)


mysql> EXPLAIN SELECT * FROM companies USE INDEX(index_companies_on_deleted_at_and_post_index_id) INNER JOIN post_indices ON post_indices.id = companies.post_index_id WHERE companies.deleted_at IS NULL ORDER BY post_indices.id LIMIT 1;
+----+-------------+--------------+------------+--------+-------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------------+--------+----------+--------------------------------------------------------+
| id | select_type | table        | partitions | type   | possible_keys                                   | key                                             | key_len | ref                                                  | rows   | filtered | Extra                                                  |
+----+-------------+--------------+------------+--------+-------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------------+--------+----------+--------------------------------------------------------+
|  1 | SIMPLE      | companies    | NULL       | ref    | index_companies_on_deleted_at_and_post_index_id | index_companies_on_deleted_at_and_post_index_id | 6       | const                                                | 638692 |   100.00 | Using index condition; Using temporary; Using filesort |
|  1 | SIMPLE      | post_indices | NULL       | eq_ref | PRIMARY                                         | PRIMARY                                         | 4       | enbro_purecrm_eu_development.companies.post_index_id |      1 |   100.00 | NULL                                                   |
+----+-------------+--------------+------------+--------+-------------------------------------------------+-------------------------------------------------+---------+------------------------------------------------------+--------+----------+--------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

更新4:

我刪除了不相關的列:

| companies | CREATE TABLE `companies` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`address` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`post_index_id` int(11) DEFAULT NULL,
`vat` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`note` text COLLATE utf8_unicode_ci,
`state` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'new',
`deleted_at` datetime DEFAULT NULL,
`lead_list_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_companies_on_vat` (`vat`),
KEY `index_companies_on_post_index_id` (`post_index_id`),
KEY `index_companies_on_state` (`state`),
KEY `index_companies_on_deleted_at` (`deleted_at`),
KEY `index_companies_on_deleted_at_and_post_index_id` (`deleted_at`,`post_index_id`),
KEY `index_companies_on_lead_list_id` (`lead_list_id`),
CONSTRAINT `fk_rails_5fc7f5c6b9` FOREIGN KEY (`lead_list_id`) REFERENCES `lead_lists` (`id`),
CONSTRAINT `fk_rails_79719355c6` FOREIGN KEY (`post_index_id`) REFERENCES `post_indices` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2523518 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

| post_indices | CREATE TABLE `post_indices` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`county` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`postal_code` int(11) DEFAULT NULL,
`group_part` int(11) DEFAULT NULL,
`group_number` int(11) DEFAULT NULL,
`group_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`city` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3101 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

更新5:

另一位開發人員在本地計算機上使用完全相同的數據集(轉儲/還原)測試了相同的查詢。 他得到了完全不同的解釋:

mysql> explain SELECT * FROM companies      INNER JOIN post_indices         ON companies.post_index_id = post_indices.id     WHERE companies.deleted_at is NULL     ORDER BY post_indices.id     LIMIT 1;
+----+-------------+--------------+-------+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+---------+----------------------------------------------------+------+-----------------------+
| id | select_type | table        | type  | possible_keys                                                                                                  | key                                             | key_len | ref                                                | rows | Extra                 |
+----+-------------+--------------+-------+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+---------+----------------------------------------------------+------+-----------------------+
|  1 | SIMPLE      | post_indices | index | PRIMARY                                                                                                        | PRIMARY                                         | 4       | NULL                                               |    1 | NULL                  |
|  1 | SIMPLE      | companies    | ref   | index_companies_on_post_index_id,index_companies_on_deleted_at,index_companies_on_deleted_at_and_post_index_id | index_companies_on_deleted_at_and_post_index_id | 11      | const,enbro_purecrm_eu_development.post_indices.id |  283 | Using index condition |
+----+-------------+--------------+-------+----------------------------------------------------------------------------------------------------------------+-------------------------------------------------+---------+----------------------------------------------------+------+-----------------------+
2 rows in set (0,00 sec)

在他的PC上進行相同的查詢是即時的。 不知道為什么會這樣。.我也嘗試過使用STRAIGHT_JOIN 當我強制post_indices表首先由MySQL讀取時,它的速度也很快。 但是對於我來說仍然是莫名其妙,為什么在另一台機器上的相同查詢很快(mysql -v 5.6.27)卻在我的機器上慢(mysql -v 5.7.10)

因此,似乎問題是MySQL使用錯誤的表作為要讀取的第一個表。

這樣效果更好嗎?

SELECT * FROM companies AS c
INNER JOIN post_indices AS pi
    ON c.post_index_id = pi.id
WHERE    c.deleted_at is NULL
ORDER BY c.post_index_id           -- Note
LIMIT 1;

INDEX(deleted_at, post_index_id)  -- note

因此, 使用 WHERE 而不運行ORDER BY可以運行多快?

使用以下優化器提示,應強制MySQL使用您的同事遵守的計划:

SELECT * FROM post_indices 
    STRAIGHT_JOIN companies FORCE INDEX(index_companies_on_deleted_at_and_post_index_id)
        ON companies.post_index_id = post_indices.id
    WHERE companies.deleted_at is NULL
    ORDER BY post_indices.id
    LIMIT 1;

如果要在post_indices的其他列上進行排序,則需要在這些列上建立索引以使此計划正常運行。

請注意,最佳方案是什么取決於delete_at為NULL的頻率。 如果delete_at經常為NULL,則上述計划將很快。 如果不是這樣,根據上述計划,在找到匹配項之前,必須經過許多行post_indices。 還要注意,對於具有OFFSET的查詢,相同的計划可能不是最有效的。

我認為這里的問題是MySQL在決定連接順序時沒有考慮ORDER BY和LIMIT的影響。 換句話說,它將選擇它認為執行完整聯接最快的聯接順序。 由於對company表有一個限制(deleted_at為NULL),所以我將從此表開始並不感到驚訝。

暫無
暫無

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

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