1. 程式人生 > >第二章 表與指標Pro SQL Server Internal (Dmitri Korotkev)

第二章 表與指標Pro SQL Server Internal (Dmitri Korotkev)

聚合索引

聚集索引就是表中資料的物理順序,它是按照聚集索引分類的。表只能定義一個聚集索引。

  如果你要在一個有資料的堆表中建立一個聚集索引,如2-5所示,第一步要做的就是SQL伺服器建立另一個根據聚集索引鍵分類的資料副本。資料頁連線到雙向連結串列,雙向連結串列的每一頁都包含指向下一頁或前一頁的指標。這個表就稱為索引的葉級,它包括實際的資料表。

 

筆記:頁面排序是由縫隙陣控制的,頁中的實際資料沒有排序。

  當葉級別由多個頁構成,SQL伺服器會建立一個索引的中級維護,如表2-6所示。

 

 

中間維護級每葉級的頁面存一行資料,包含兩條資訊:實體地址和從頁中獲得的索引鍵最小值。唯一例外的只有最先前的第一頁的第一行,這個位置

SQL伺服器存空值,不是最小索引鍵值。優化過後,當你要用最小鍵值插入新的行時,SQL伺服器不需要更新非葉級的行。

   中間維護級也連線到雙向連結串列,SQL伺服器天可以一直新增中間維護級的頁面直到它只包含單獨頁面的級別,這個級別稱為根級。它也是索引的新增點,如圖2-7所示。

  可以發現,索引總是隻有一個葉級,一個根級,無或多箇中間維護級。唯一例外的是當索引資料納入單獨的頁面。這時候SQL伺服器不會建立一個單獨的根頁面,而且索引只有單獨的葉級頁面。

  索引頁面的數量很大程度依賴於行和索引鍵的大小。例如4位元組整數列的鍵在葉中間維護級和根級需要每行

13位元組的大小,這13位元組有2位元組縫隙陣入口,4位元組索引鍵的值和6位元組的頁指標,還有1位元組行開銷,它用來確保空間充足,因為索引鍵不包括可變長度列和空列。

  結果表明,它能適應8060位元組除以13位元組每行,等於620行每頁。它說明用一箇中間維護級頁能儲存620*620=384,400葉級頁面的資訊。如果資料行是200位元組,你能每葉級頁面存40行,而且僅用3級索引可高達15,376,000行。新增另一箇中間維護級到索引中,能基本覆蓋所有的整數值。

  筆記:在現實中,索引碎片會減少這些數量。我們在第六章會提到索引碎片。

SQL伺服器有三種不同的從索引中讀取資料。第一種是通過排序掃描。我們假設我們要執行這段程式碼:

SELECT Name FROM dbo.Customers ORDER BY CustomerId query. 索引的頁級資料已經按Customer Id列的值排序好了。結果就是SQL伺服器能從第一頁到最後一頁掃描索引的頁級頁面,並按原來儲存的順序返回行。、

SQL伺服器從索引的根級頁面開始讀取第一行,行用鍵最小值參照中間維護級頁面。SQL伺服器讀取頁面並重復步驟知道它找到頁級第一頁。然後SQL伺服器開始一行接著一行讀取行,按照頁面連線表移動知道所有的行都讀取了。圖2-8闡述了這一過程。

 

 前置查詢的查詢計劃顯示聚類索引掃描操作員設定排序屬性集為true,如圖2-9

 

  值得一提的是條款的順序不是要引發掃描排序。掃描排序只是把SQL伺服器基於索引鍵的順序讀取資料。SQL伺服器能夠從向前或向後兩個方向同時索引導航,然而一定要記住SQL伺服器在向後索引掃描時不能並行使用。

  提示:可以通過查詢計劃測試索引掃描屬性或索引查詢屬性來檢查掃描的方向。記住,Management Stdio不會通過圖表顯示這些屬性。你要開啟屬性視窗,選中操作窗體的執行計劃並且選中View屬性窗體選單專案,或者F4按鈕才能看到。

  企業版的SQL伺服器有一個叫做 merry-go-roundscan的優化屬性呢能過允許多個任務共享同一個索引掃描。假設S1會話在掃描索引,有時候在掃描中,另一個S2會話執行查詢需要掃描同一個索引,通過 merry-go-roundscan會話S2S1現有的位置加入S1掃描中。SQL伺服器每頁只讀取一次掃描,同時進行兩個會話。

  當S1掃描到了索引末端,S2開始從索引首段開始掃描資料,直到指標指向它開始的位置。 merry-go-roundscan是另一個為什麼不能依靠索引鍵順序的例子,也是為什麼要特定ORDER BY條款。下一個存取方式在排序掃描被叫做分配順序掃描之後,SQL伺服器通過IAM頁面來存取表格資料,它類似堆表的方式。e SELECT Name FROM dbo.Customers WITH (NOLOCK)查詢語句和表2-10說明了這個方法。表2-11是這個語句的執行計劃。

  不巧的是,當SQL伺服器在使用分配順序掃描的時候不容易察覺,儘管Ordered 屬性在執行計劃的時候顯示失敗,它還是表明SQL伺服器不管是不是按索引鍵順序讀取行,還是按分配順序掃描。

  另一個分配順序掃描在掃描大量的表格的時候會更快,儘管啟動的時候會有更大的開銷,在表單不多的時候SQL伺服器不用這種存取方式。另一個重要的考慮因素就是資料連貫性,SQL伺服器在表單有聚類索引的時候不使用前向指標,分配順序掃描會導致不連貫,因為頁面切割後可能會跳過行或重複讀取行。結果就是SQL伺服器一般避免使用分配順序掃描,除非需要讀取的資料是讀未提交或者可序列化的事務隔離級別。

  筆記:我們會在第六章的“指標碎片”一節談到頁面切割和碎片,還會在第三節“鎖定、封鎖、併發”提到鎖定和資料連貫性。

  最後的索引儲存方法叫做索引查詢,SELECT Name FROM dbo.Customers WHERE CustomerId BETWEEN 4 AND 7”查詢語句和圖2-12說明了操作。

 

  為了從表中讀取一定範圍的行,SQL伺服器用最小鍵值需要找到行,這個最小值為4SQL伺服器從根頁面開始,它的第二行參照頁面最小鍵值350。這個比我們在尋找(4)需要更大的鍵值,SQL伺服器參照根頁面的第一行讀取中間維護級頁面(1:170)。

  類似的中間維護級頁面引導SQL伺服器到第一個葉級頁面(1:176)。SQL伺服器讀取頁面然後用CustomerIds e等於45來讀取行,最後在第二頁讀取到兩行。

  執行計劃如如2-13

  可以看出索引查詢比索引掃描更有效率,因為SQL伺服器程序只是行和資料頁的子集,而不是臊面整個表。

  從技術角度出發,有兩種索引查詢操作。第一種是單例查詢,或者指標查詢,它使SQL伺服器查詢返回單個行。以WHERE CustomerId = 2 為例,另一個型別的索引查詢叫做範圍掃描,它使SQL伺服器找到鍵的最大和最小值並掃描(前向後向掃描)行的集合直到到達掃描範圍的末端。 查詢語句”WHERE CustomerId BETWEEN 4 AND 7 “進行範圍掃描,這兩種情況在執行計劃中都屬於索引查詢。

  你能猜到,範圍掃描完全可能讓SQL伺服器處理索引中大量甚至全部資料。例如你改變查詢命令為 WHERE CustomerId > 0SQL伺服器會讀取所有頁所有行,儘管能用索引查詢顯示執行計劃,還是要記得在查詢語句效能調節時,分析範圍掃描的效率。

  關係資料庫有一個概念叫 SARGable 預測,代表搜尋引數,這個預測是SARGableSQL伺服器能耐隔離出單獨值或範圍的索引鍵值時,在預測評價時限制查詢。顯然,SARGable預測還有利用索引查詢對寫查詢語句很有利。

   SARGable 預測包括這些操作符號:= , > , >= , < , <= , IN , BETWEEN ,  LIKE(防止前置匹配)。非SARGable操作符包括:NOT , <> , LIKE(防止非前置匹配),NOT IN

  非SARGable預測時的另一種情況是使用功能或數學計算與表中的列產生衝突,sql伺服器必須呼叫該函式或對其處理的每行執行計算。幸運的是,在某些情況下您可以重構查詢來預測SARgable。表2-1列舉了一些例子。

要記住的另一個重要因素是型別轉換,在某些情況下,您可以通過使用不正確的資料型別來預測非SARGable。我們建立一個帶有可變長字元列的表並填充一些資料,如2-6

 

聚集索引鍵列定義為可變長字元,雖然它儲存的是整型值。現在,讓我們進行兩個選擇,如表2-7,檢視執行計劃。

如圖2-14,在整數引數的情況下,S QL 伺服器掃描聚集索引,將每行的可變長字元型轉換為整型。在第二種情況下,S QL Server將整數引數轉換為可變長字元型並且利用更有效的聚類索引搜尋操作。

 

  注意在加入預測中的列資料型別,隱式或顯式的資料型別轉換可以顯著降低查詢的效能。您會觀察到非常相似的行為,在Unicode字串引數的情況下。執行2-8表中的執行語句,圖2-15顯示宣告的執行計劃。

  正如您所看到的,Unicode字串引數對於varchar列是非SARGable 。這是一個比表面看來更大的問題。雖然很少用這種方式寫查詢語句,如表2-8,現在大多數應用程式開發環境將字串視為UnicodeSql伺服器客戶端庫對於字串物件產生Unicodenvarchar)引數,除非引數資料型別明確指定為可變長字元型.使預測非SARGable,而且由於不必要的掃描,它會導致主要的效能命中即使是在可變長字元型列被索引的時候。

  重要提示:始終在客戶端應用程式中指定引數資料型別。例如在 ADO.Net,中使用Parameters.Add("@ParamName",SqlDbType.Varchar, <Size>).Value = stringVariable,而不是超載的Parameters.Add("@ParamName").Value = stringVariable orm框架中使用對映來顯式指定類中的非unicode屬性。

  值得一提的是可變長字元型別引數可用於nvarchar Unicode資料列