![](/img/trans.png)
[英]Why does MySQL View and the same View's underlying SELECT query return different results?
[英]MySQL: why is a query using a VIEW less efficient compared to a query directly using the view's underlying JOIN?
我有三個表, bug
, bugrule
和bugtrace
,它們的關系是:
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.