有人可以解釋為什麼在mysql中加入兩個視圖這麼慢嗎?
這是我昨天問的一個問題 - https://stackoverflow.com/questions/22180727/left-joining-two-views-is-slow。
我得到了一個對我有幫助的好答案,但我不明白為什麼 LEFT JOIN 比查找慢得多。LEFT JOIN 是 16 秒——我很確定我的表至少優化了 90%——在進行查找時只需 0.14 秒。當我 LEFT JOIN 表時,它並沒有這麼慢那麼為什麼要查看?
MySQL Server 5.6 中提供了視圖(包括可更新視圖)。視圖是儲存的查詢,在呼叫時會產生結果集。視圖充當虛擬表。
關於視圖,首先必須意識到的是它產生了一個結果集。從視圖呼叫的查詢中產生的結果集是一個虛擬表,因為它是按需創建的。之後沒有可以呼叫的 DDL 來立即索引結果集。出於所有意圖和目的,結果集是一個沒有任何索引的表。實際上,您正在執行的 LEFT JOIN 基本上是帶有一些過濾的笛卡爾積。
為了讓您更詳細地了解兩個視圖的 JOIN,我將參考我去年發表的一篇文章,解釋 MySQL 用於評估 JOIN 和 WHERE 的內部機制(在 JOIN 條件和 WHERE 條件之間存在執行差異嗎?)。我將向您展示在Understanding MySQL Internals (Page 172)中發布的機制:
- 確定哪些鍵可用於從表中檢索記錄,並為每個表選擇最佳鍵。
- 對於每個表,確定表掃描是否比讀取鍵更好。如果匹配key值的記錄很多,key的優勢就降低了,表掃描變快了。
- 當查詢中存在多個表時,確定應連接表的順序。
- 重寫 WHERE 子句以消除死程式碼,減少不必要的計算並儘可能更改約束以打開使用鍵的方式。
- 從聯接中消除未使用的表。
- 確定鍵是否可用於
ORDER BY
和GROUP BY
。- 嘗試簡化子查詢,並確定可以將其結果記憶體到何種程度。
- 合併視圖(將視圖引用擴展為宏)
好的,似乎應該使用索引。不過,仔細看看。如果你用 word
View
代替Table
,看看機制的執行會發生什麼:機制修改
- 確定可用於從中檢索記錄的鍵**
views
,並為每個鍵選擇最佳鍵view
**。- 對於每個**
view
,確定view
掃描是否比讀取鍵更好。如果匹配key值的記錄很多,key的優勢就會降低,view
**掃描速度會變快。- 當查詢中存在**
views
多個時,確定應連接的順序。views
**- 重寫 WHERE 子句以消除死程式碼,減少不必要的計算並儘可能更改約束以打開使用鍵的方式。
- 從聯接中消除未使用**
views
**的。- 確定鍵是否可用於
ORDER BY
和GROUP BY
。- 嘗試簡化子查詢,並確定可以將其結果記憶體到何種程度。
- 合併視圖(將視圖引用擴展為宏)
每個表(視圖)都沒有索引。因此,使用虛擬表、臨時表或沒有索引的表在執行 JOIN 時確實變得模糊不清。使用的鍵僅用於 JOIN 操作,而不是用於更快地查找內容。
將您的查詢想像為拿起兩本電話簿,即 2014 年黃頁和 2013 年黃頁。每本黃頁書都包含住宅電話號碼白頁。
2012 年底,使用數據庫表生成 2013 年黃頁。
2013 年期間
- 人們更改了電話號碼
- 人們收到了新的電話號碼
- 人們放棄了電話號碼,轉而使用手機
2013 年底,使用數據庫表生成 2014 年黃頁。
顯然,這兩個電話簿之間存在差異。對數據庫表進行 JOIN 以找出 2013 年和 2014 年之間的差異應該沒有問題。
想像一下手動合併兩個電話簿以找出差異。聽起來很瘋狂,不是嗎?儘管如此,當您加入兩個視圖時,這正是您要求 mysqld 執行的操作。請記住,您沒有加入真正的表,也沒有可供捎帶的索引。
現在,讓我們回顧一下實際的查詢。
SELECT DISTINCT viewA.TRID, viewA.hits, viewA.department, viewA.admin, viewA.publisher, viewA.employee, viewA.logincount, viewA.registrationdate, viewA.firstlogin, viewA.lastlogin, viewA.`month`, viewA.`year`, viewA.businesscategory, viewA.mail, viewA.givenname, viewA.sn, viewA.departmentnumber, viewA.sa_title, viewA.title, viewA.supemail, viewA.regionname FROM viewA LEFT JOIN viewB ON viewA.TRID = viewB.TRID WHERE viewB.TRID IS NULL
您正在使用一個虛擬表(沒有索引的表)viewA,將它連接到另一個虛擬表 viewB。間歇性生成的臨時表將與 viewA 一樣大。然後,您在大型臨時表上執行內部排序以使其與眾不同。
結語
考慮到評估 JOIN 的內部機制,以及視圖結果集的瞬態和無索引特性,您的原始查詢(兩個視圖的 LEFT JOIN)應該獲得數量級的執行時間。同時,考慮到我剛剛描述的相同 JOIN 算法,您從 StackOverflow 得到的答案應該表現良好。
我希望我剛剛發布的血腥細節能回答你關於為什麼的問題。