Performance

SQL Server 嵌套視圖,多深才算太多?

  • November 17, 2020

我偶然發現了一個使用視圖的問題。我的程序與之互動的內容管理系統定義了一個視圖,該視圖呼叫系統上的另一個視圖。那是從2個級別開始的。在我偶然發現視圖之前,我認為嵌套視圖很隨意,但是在我的程式碼爆炸並且我四處搜尋之後,我開始拿起與嵌套視圖相關的“罪”、“粗俗”和“壞”之類的詞。

好吧,我從兩級嵌套視圖開始。在我的應用程序級別,我定義了我的第 3 級,那是它給我超時錯誤的時候。3級是不是太多了?這些表不是很大——在 5,000 到 100,000 條記錄之間,而我的視圖本身只產生 2,000 條記錄。這是要打斷駱駝的背嗎?

問題不是視圖級別的數量,而是結果查詢的複雜性。擁有大量嵌套視圖可能會增加查詢的複雜性,但很大程度上取決於這些視圖實際在做什麼。

當您在做不必要的或重複的工作時,擁有多層嵌套視圖通常會產生問題。例如,如果底層視圖連接表 A、B 和 C,而您只需要來自 A 和 B 的資訊,則連接到 C 意味著數據庫可能必須做不必要的工作(取決於數據庫引擎和確切的定義) ,優化器可能能夠確定連接到 C 是不必要的並消除它,但這通常不會發生)。如果第一級視圖連接 A 和 B,然後第二級視圖再次連接到 B,因為它想從 B 添加一些額外的列,那麼您現在已經強制數據庫執行重複工作併兩次連接到 B。當您在不同級別的視圖中有謂詞強制數據庫執行它不必執行的工作時,也會發生同樣的事情。

從廣義上講,如果你有一系列精心設計的視圖,其中每個級別都有一些明確的職責,那麼你更有可能獲得合理的性能,而不是你有一組不連貫的視圖,這些視圖隨著時間的推移隨著不同的開發人員添加而有機地成長起來事情以不同的觀點來滿足當天的要求。假設您有一個包含以下對象的錯誤跟踪系統

  • 一張bug有所有問題的桌子,
  • vw_bug在此之上的視圖充當抽象層,以防有人想要重構bug
  • 過濾掉軟刪除行的vw_current_bug視圖vw_bug
  • vw_open_bug向下過濾以打開錯誤的視圖
  • vw_bug_overview連接vw_open_bug到所有查找表以顯示概覽頁面資訊的視圖

這是四層視圖,每一層都提供一個定義良好的抽象層。從性能的角度來看,這可能沒問題。

當優化器難以或不可能找出應用謂詞的最佳位置時,擁有多個級別的視圖也會給數據庫帶來問題(Google“謂詞推送”以獲得大量關於此的附加資訊)。如果您有許多不同級別的視圖,但where table_a_primary_key = :1在查詢外部視圖時始終指定謂詞,則優化器必須確定何時應用該謂詞。如果table_a是內部視圖中的驅動表,理想情況下,優化器會確定它希望將該謂詞向下推送到所有視圖層,並將其應用於該驅動表,然後再執行連接工作。然而,根據數據庫引擎、版本和視圖中程式碼的複雜性,優化器可能很難或不可能確定它想要推送謂詞或將其推送到那里而不從理論上改變結果。不同的數據庫引擎和這些引擎的不同版本在這方面會有非常不同的行為。

從廣義上講,這與當您擁有一個恰好實現相對複雜邏輯的單一視圖時所面臨的挑戰沒有什麼不同。不同的數據庫引擎有不同的方法讓你規避這些問題——Oracle 提示可以強制優化器推送一個謂詞,Oracle 讓你定義一個類似於視圖的管道表函式,但允許你傳入一個你應用的參數在你想要的地方,SQL Server 表函式的行為方式大致相同,等等。

這些並不是擁有一堆視圖級別的唯一兩個潛在問題,但這是我的經驗中的兩個大問題。與任何查詢一樣,查詢越複雜,優化器就越難以詳盡地搜尋每個可能的查詢計劃,而且您最終得到的計劃不是最優的可能性就越大。查詢越複雜,您就越有可能遇到優化器無法正確計算各個步驟的基數的極端情況,因為它不知道如何正確組合統計資訊。

另一方面,設計良好的視圖集可以提供有用的抽象,從而更容易正確定義查詢。在早期的錯誤跟踪系統中,vw_current_bug提供了一個有用的抽象層,這樣您就不必在數百個查詢中過濾掉軟刪除的行。在某些情況下,這個抽象層會產生性能成本,但這可能是一個很小的成本,您很樂意為避免重複程式碼而付出代價。

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