簡體   English   中英

MySQL:與直接使用視圖的底層 JOIN 的查詢相比,為什么使用 VIEW 的查詢效率較低?

[英]MySQL: why is a query using a VIEW less efficient compared to a query directly using the view's underlying JOIN?

我有三個表, bugbugrulebugtrace ,它們的關系是:

bug     1--------N  bugrule
        id = bugid

bugrule 0---------N bugtrace
        id = ruleid

因為我幾乎總是對bug <---> bugtrace之間的關系感興趣, bug <---> bugtrace我創建了一個適當的VIEW ,用作多個查詢的一部分。 有趣的是,使用此VIEW查詢比顯式使用底層JOIN等效查詢的性能要差得多。

VIEW定義:

CREATE VIEW bugtracev AS
  SELECT t.*, r.bugid
      FROM bugtrace AS t
      LEFT JOIN bugrule AS r ON t.ruleid=r.id
    WHERE r.version IS NULL

使用VIEW的查詢的執行計划(性能不佳):

mysql> explain 
      SELECT c.id,state,
             (SELECT COUNT(DISTINCT(t.id)) FROM bugtracev AS t 
               WHERE t.bugid=c.id) 
       FROM bug AS c 
      WHERE c.version IS NULL
        AND c.id<10;
+----+--------------------+-------+-------+---------------+--------+---------+-----------------+---------+-----------------------+
| id | select_type        | table | type  | possible_keys | key    | key_len | ref             | rows    | Extra                 |
+----+--------------------+-------+-------+---------------+--------+---------+-----------------+---------+-----------------------+
|  1 | PRIMARY            | c     | range | id_2,id       | id_2   | 8       | NULL            |       3 | Using index condition |
|  2 | DEPENDENT SUBQUERY | t     | index | NULL          | ruleid | 9       | NULL            | 1426004 | Using index           |
|  2 | DEPENDENT SUBQUERY | r     | ref   | id_2,id       | id_2   | 8       | bugapp.t.ruleid |       1 | Using where           |
+----+--------------------+-------+-------+---------------+--------+---------+-----------------+---------+-----------------------+
3 rows in set (0.00 sec)

直接使用底層JOIN的查詢執行計划(性能好):

mysql> explain 
       SELECT c.id,state,
              (SELECT COUNT(DISTINCT(t.id)) 
                 FROM bugtrace AS t
                 LEFT JOIN bugrule AS r ON t.ruleid=r.id 
                WHERE r.version IS NULL
                  AND r.bugid=c.id) 
        FROM bug AS c 
       WHERE c.version IS NULL
         AND c.id<10;
+----+--------------------+-------+-------+---------------+--------+---------+-------------+--------+-----------------------+
| id | select_type        | table | type  | possible_keys | key    | key_len | ref         | rows   | Extra                 |
+----+--------------------+-------+-------+---------------+--------+---------+-------------+--------+-----------------------+
|  1 | PRIMARY            | c     | range | id_2,id       | id_2   | 8       | NULL        |      3 | Using index condition |
|  2 | DEPENDENT SUBQUERY | r     | ref   | id_2,id,bugid | bugid  | 8       | bugapp.c.id |      1 | Using where           |
|  2 | DEPENDENT SUBQUERY | t     | ref   | ruleid        | ruleid | 9       | bugapp.r.id | 713002 | Using index           |
+----+--------------------+-------+-------+---------------+--------+---------+-------------+--------+-----------------------+
3 rows in set (0.00 sec)

CREATE TABLE語句(由不相關的列減少)是:

mysql> show create table bug;
CREATE TABLE `bug` (
  `id` bigint(20) NOT NULL,
  `version` int(11) DEFAULT NULL,
  `state` varchar(16) DEFAULT NULL,
  UNIQUE KEY `id_2` (`id`,`version`),
  KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

mysql> show create table bugrule;
CREATE TABLE `bugrule` (
  `id` bigint(20) NOT NULL,
  `version` int(11) DEFAULT NULL,
  `bugid` bigint(20) NOT NULL,
  UNIQUE KEY `id_2` (`id`,`version`),
  KEY `id` (`id`),
  KEY `bugid` (`bugid`),
  CONSTRAINT `bugrule_ibfk_1` FOREIGN KEY (`bugid`) REFERENCES `bug` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

mysql> show create table bugtrace;
CREATE TABLE `bugtrace` (
  `id` bigint(20) NOT NULL,
  `ruleid` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `ruleid` (`ruleid`),
  CONSTRAINT `bugtrace_ibfk_1` FOREIGN KEY (`ruleid`) REFERENCES `bugrule` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

您會問為什么要對具有COUNT(DISTINCT val)和相關子查詢的幾個復雜查詢進行查詢優化。 很難確定為什么

不過,您可能會通過擺脫依賴子查詢來解決大部分性能問題。 嘗試這樣的事情:

 SELECT c.id,state, cnt.cnt
   FROM bug AS c
   LEFT JOIN (
            SELECT bugid, COUNT(DISTINCT id) cnt
              FROM bugtracev 
             GROUP BY bugid
        ) cnt ON c.id = cnt.bugid
  WHERE c.version IS NULL
    AND c.id<10;

為什么這有幫助? 為了滿足查詢,優化器可以選擇只運行一次GROUP BY子查詢,而不是多次。 而且,您可以在GROUP BY子查詢上使用EXPLAIN來了解其性能。

您還可以通過在bugrule上創建與視圖中的查詢匹配的復合索引來提高性能。 試試這個。

 CREATE INDEX bugrule_v ON bugrule (version, ruleid, bugid)

並嘗試像這樣切換最后兩列

 CREATE INDEX bugrule_v ON bugrule (version, ruleid, bugid)

這些索引稱為覆蓋索引,因為它們包含滿足查詢所需的所有列。 version首先出現,因為這有助於優化視圖定義中的WHERE version IS NULL 這使它更快。

專業提示:避免在視圖和查詢中使用SELECT * ,尤其是當您遇到性能問題時。 相反,列出您實際需要的列。 *可能會強制查詢優化器避免覆蓋索引,即使索引會有所幫助。

使用 MySQL 5.6(或更早版本)時,請嘗試至少使用 MySQL 5.7。 根據MySQL 5.7 中的新增功能?

我們必須在很大程度上統一對派生表和視圖的處理。 到目前為止,FROM 子句(派生表)中的子查詢是無條件物化的,而從相同查詢表達式創建的視圖有時會被物化,有時會合並到外部查詢中。 這種行為除了不一致之外,還會導致嚴重的性能損失。

暫無
暫無

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

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