Mysql

有人可以解釋為什麼在mysql中加入兩個視圖這麼慢嗎?

  • September 14, 2017

這是我昨天問的一個問題 - https://stackoverflow.com/questions/22180727/left-joining-two-views-is-slow

我得到了一個對我有幫助的好答案,但我不明白為什麼 LEFT JOIN 比查找慢得多。LEFT JOIN 是 16 秒——我很確定我的表至少優化了 90%——在進行查找時只需 0.14 秒。當我 LEFT JOIN 表時,它並沒有這麼慢那麼為什麼要查看?

根據關於視圖的 MySQL 文件

MySQL Server 5.6 中提供了視圖(包括可更新視圖)。視圖是儲存的查詢,在呼叫時會產生結果集。視圖充當虛擬表。

關於視圖,首先必須意識到的是它產生了一個結果集。從視圖呼叫的查詢中產生的結果集是一個虛擬表,因為它是按需創建的。之後沒有可以呼叫的 DDL 來立即索引結果集。出於所有意圖和目的,結果集是一個沒有任何索引的表。實際上,您正在執行的 LEFT JOIN 基本上是帶有一些過濾的笛卡爾積。

為了讓您更詳細地了解兩個視圖的 JOIN,我將參考我去年發表的一篇文章,解釋 MySQL 用於評估 JOIN 和 WHERE 的內部機制(在 JOIN 條件和 WHERE 條件之間存在執行差異嗎?)。我將向您展示在Understanding MySQL Internals (Page 172)中發布的機制:

  • 確定哪些鍵可用於從表中檢索記錄,並為每個表選擇最佳鍵。
  • 對於每個表,確定表掃描是否比讀取鍵更好。如果匹配key值的記錄很多,key的優勢就降低了,表掃描變快了。
  • 當查詢中存在多個表時,確定應連接表的順序。
  • 重寫 WHERE 子句以消除死程式碼,減少不必要的計算並儘可能更改約束以打開使用鍵的方式。
  • 從聯接中消除未使用的表。
  • 確定鍵是否可用於ORDER BYGROUP BY
  • 嘗試簡化子查詢,並確定可以將其結果記憶體到何種程度。
  • 合併視圖(將視圖引用擴展為宏)

好的,似乎應該使用索引。不過,仔細看看。如果你用 wordView代替Table,看看機制的執行會發生什麼:

機制修改

  • 確定可用於從中檢索記錄的鍵**views,並為每個鍵選擇最佳鍵view**。
  • 對於每個**view,確定view掃描是否比讀取鍵更好。如果匹配key值的記錄很多,key的優勢就會降低,view**掃描速度會變快。
  • 當查詢中存在**views多個時,確定應連接的順序。views**
  • 重寫 WHERE 子句以消除死程式碼,減少不必要的計算並儘可能更改約束以打開使用鍵的方式。
  • 從聯接中消除未使用**views**的。
  • 確定鍵是否可用於ORDER BYGROUP 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 得到的答案應該表現良好。

我希望我剛剛發布的血腥細節能回答你關於為什麼的問題。

引用自:https://dba.stackexchange.com/questions/60203