1. 程式人生 > >《Pro SQL Server Internals》部分翻譯(P155-165)

《Pro SQL Server Internals》部分翻譯(P155-165)

本文選自《Pro SQL Server Internals》

作者: Dmitri Korotkevitch

出版社: Apress

出版年: 2016-12-29

作者簡介:Dmitri Korotkevitchis是微軟SQL Server MVP和微軟認證大師。作為應用程式和資料庫開發人員、資料庫管理員和資料庫架構師,他具有多年使用SQL Server的經驗。他專門從事OLTP系統在高負載下的設計、開發和效能調優。Dmitri經常在各種Microsoft和SQL PASS活動上發言,他為世界各地的客戶提供SQL Server培訓。

原文連結:http://www.doc88.com/p-4042504089228.html

 

第七章

設計和優化索引

定義一種應用於所有地方的索引策略是不可能的。每個系統都是獨特的,需要基於工作,業務需求和其他一些因素的自己的索引方法。然而,有幾個設計的注意事項和指導方針可以被應用到每個系統。

   在我們優化現有的系統時非常正解。雖然優化是一個迭代過程在任何時候都是獨特的,但是有一組技術可以用來檢測每個資料系統的效率低下。

在本章節,請記住我們將呈現一些重要因素在設計新的索引和優化現有的系統時。

 

聚集索引設計注意事項

在你改變聚集索引鍵的值時,將會發生兩件事。首先,SQL server移動行到聚集索引頁鏈和資料檔案中的不同位置。第二,它更新聚集索引鍵的行編號。這個行編號被儲存,而且被再次更新在非聚集索引裡。就I/O而言,這可能非常昂貴,尤其是在批處理更新的情況下。另外,它還會增加聚集索引的碎片,在行編號增加下,還增加非聚集索引的碎片。因此,最好有一個靜態聚集索引,讓鍵的值不會改變。

所有非聚集索引使用聚集索引鍵作為行編號。一個太大的聚集索引鍵增加非聚集索引行的儲存空間。結果,在索引和距離掃描時SQL server不得不處理更多的資料頁,這將導致效率低下。

在非唯一非聚集索引情況下,行編號同樣儲存在非葉索引層上,反過來,這將減少每頁的索引記錄,並且導致索引中額外的中間級別。在每次SQLserver遍歷非聚集索引的二叉樹時,儘管非葉索引層快取在記憶體中,但是都會引入額外的邏輯讀取。

最後,在索引維護時,越來越多的非聚集索引在緩衝池中使用更多空間和引入更多的開銷。顯然,不可能提供一個通用閾值來定義可以應用於任何表的鍵的最大可接受大小。但是,一般來說,最好使用一個窄的聚集索引鍵,索引鍵越小越好。將索引鍵定義為唯一的大有益處。這個重要原因並非顯而易見的。考慮這個場景,在表沒有唯一的索引時,而你希望執行使用執行計劃中非聚集索引查詢的查詢。在這個情況下,如果非聚集索引中的行編號不是惟一的,SQL Server將不知道在鍵查詢操作期間選擇哪個聚集索引行。SQL Server通過向非惟一聚集索引中新增另一個名為唯一識別符號的可空整數列來解決此類問題。對於鍵值的第一次出現,SQL Server用NULL填充唯一識別符號,併為插入到表中的每個後續重複值自動遞增。

 

CHAPTER 7 ■ DESIGNING AND TUNING THE INDEXES

第七章 ■ 設計和優化索引

注意:每個聚集索引鍵的值的可能的重複次數受整型域值的限制。你不能有超過2,147,483,648行使用相同的聚集索引鍵。這是一個理論上的限制,建立選擇性如此差的索引顯然不是一個好主意。

讓我們看看在非惟一聚集索引中引入唯一識別符號的開銷。清單7-1所示的程式碼建立了三個相同結構的不同表,每個表都填充了65,536行。UniqueCI表是唯一被定義了唯一聚集索引。nonuniquecinodups表沒有任何重複的鍵值。最後,NonUniqueCDups表在索引中有大量的副本。

清單7 - 1非惟一聚集索引:表建立

現在,讓我們看看每個表的聚集索引的物理統計資訊。程式碼如清單7-2所示,結果如圖7-1所示。

清單7 - 1非唯一聚集索引:檢查聚集索引的行大小

儘管在表NonUniqueCINoDups沒有重複的鍵,但是行仍然增加了2個額外的位元組。SQL Server在資料的變長部分儲存一個唯一識別符號,這2個位元組由變長資料偏移陣列中的另一個條目新增。

在這種情況下,當聚集索引具有重複值時,唯一識別符號將再新增4個位元組,即造成總共6個位元組的開銷。值得一提的是,在某些特殊情況下,唯一識別符號使用的額外儲存空間可以減少資料頁上可以容納的行數。我們的示例演示了這種情況。如你所見,UniqueCI表與其他兩個表相比,UniqueCI表使用的資料頁少了大約15%。206/5000  

現在,讓我們看唯一識別符號如何影響非聚集索引。清單7-3所示的程式碼在所有三個表中建立了非叢集索引。圖7-2顯示了這些索引的物理統計資訊。

   清單7-3非唯一聚集索引:檢查非聚集索引的行大小

 

 

 

圖7 - 2非唯一聚集索引:非聚集索引的行大小

在NonUniqueCINoDups表沒有非聚集索引沒有開銷。正如你所記得的,SQLserver不會儲存偏移量資訊到為了儲存空資料的尾隨列的變長陣列中。儘管如此,在NonUniqueCIDups表中唯一識別符號引入了8子節的開銷。這8個位元組由一個4位元組的唯一識別符號值、一個2位元組的變長資料偏移陣列條目和一個儲存行中變長列數的2位元組條目組成。我們可以用以下方式總結唯一識別符號的儲存開銷。對於唯一識別符號為空的行,如果索引中至少有一個儲存非空值的變長列,則會有兩個位元組的開銷。這個開銷來自變長偏移陣列條目為了唯一識別符號所在行,否則將不會有開銷。在填充唯一識別符號的情況下,如果存在儲存非空值的變長列,則開銷為6位元組。否則,開銷是8位元組。

提示:如果期望在聚集索引值中有大量重複,可以將整數標識列作為最右邊的列新增到索引中,從而使其惟一。與唯一識別符號引入的最多8位元組的不可預測儲存開銷相比,這將為每行增加一個4位元組的可預測儲存開銷。當您通過引用該行的所有聚集索引列時,還可以提高單個查詢操作的效能。

以最小化插入新行導致的索引碎片的方式設計聚集索引是有益的。實現這一點的方法之一是使聚集索引值不斷增加。標識列上的索引就是這樣一個例子。另一個例子是時間列,其中填充了插入時的當前系統時間。

然而,索引不斷增長存在兩個潛在問題。第一個與統計有關。

正如第三章所學的,當直方圖中沒有引數值時,SQL Server中的遺留基數估計值會低估基數。您應該將這種因素考慮到系統的統計維護策略中,除非您使用新的SQL Server 2014-2016基數估計值,該估計值假定直方圖之外的資料具有類似於表中其他資料的分佈。下一個問題更復雜。隨著索引的增加,資料總是插入索引的末尾。一方面,它可以防止頁面分裂,減少碎片。另一方面,它可能導致熱點,即當多個會話試圖修改相同的資料頁和分配新的頁或區段時發生的序列化延遲。SQL Server不允許多個會話更新相同的資料結構,而是序列化這些操作。熱點通常不是問題,除非系統以非常高的速率收集資料,並且索引每秒處理數百次插入。我們將在第27章“系統故障排除”中討論如何檢測此類問題。

   最後,如果系統有一組頻繁執行的重要查詢,那麼考慮聚集索引可能是有益的,它可以優化這些查詢。這消除了昂貴的鍵查詢操作,並提高了系統的效能。

     儘管可以通過使用覆蓋非聚集索引來優化此類查詢,但它並不總是理想的解決方案。在某些情況下,需要建立非常大的非聚集索引,這將佔用磁碟和緩衝池中的大量儲存空間。另一個重要因素是修改列的頻率。將經常修改的列新增到非聚集索引需要SQL Server在多個位置更改資料,這會對系統的更新效能產生負面影響,並增加阻塞。

儘管如此,設計滿足所有這些指導原則的聚集索引並不總是可能的。此外,您不應該將這些指導方針視為絕對的要求。您應該分析系統、業務需求、工作負載和查詢,並選擇對您有利的叢集索引,即使它們違反了其中的一些指導原則。

識別符號、序列和惟一識別符號

人們通常選擇識別符號、序列和惟一識別符號作為聚集索引鍵。與往常一樣,這種方法有其優缺點。在這些列上定義的聚集索引是惟一的、靜態的和窄的。此外,恆等式和序列不斷增加,這減少了索引碎片。其中一個最理想的用例是目錄實體表。例如,可以考慮儲存客戶、文章或裝置列表的表。這些表儲存數千行,甚至可能幾百萬行,儘管資料是相對靜態的,因此,熱點不是問題。此外,這些表通常由外來鍵引用並用於連線。整數列或大數列上的索引非常緊湊和高效,這將提高查詢的效能。

注意,我們將在第8章“約束”中更詳細地討論外來鍵約束。

識別符號或序列列上的聚集索引在事務表的情況下效率較低,事務表以非常高的速率收集大量資料,這是由於它們引入的潛在熱點。另一方面,對於聚集索引和非聚集索引,唯一的識別符號很少是一個好的選擇。使用NEWID()函式生成的隨機值極大地增加了索引碎片。此外,惟一識別符號上的索引會降低批處理操作的效能。讓我們看一個示例並建立兩個表:一個表在標識列上具有聚集索引,另一個表在唯一的識別符號列上具有聚集索引。下一步,我們將在兩個表中插入65,536行。您可以在清單7-4中看到執行此操作的程式碼。

清單7 - 4唯一的識別符號:表建立

我的計算機上的執行時間和讀取次數如表7-1所示。圖7-3顯示了這兩個查詢的執行計劃。

表7 - 1將資料插入表中:執行統計資訊

 

圖7-3將資料插入表:執行計劃

如您所見,在唯一的識別符號列上的索引的情況下,還有另一個排序操作符。SQL Server在插入之前對隨機生成的唯一的識別符號值進行排序,這會降低查詢的效能。

讓我們向表中插入另一批行並檢查索引碎片。執行此操作的程式碼如清單7-5所示。圖7-4顯示了查詢的結果

清單7 - 5惟一識別符號:插入行並檢查碎片

圖7 - 4索引碎片化

正如您所看到的,唯一識別符號列上的索引非常分散,與識別符號列上的索引相比,它使用的資料頁多了大約40%。在唯一識別符號列上的索引中進行批量插入,會在資料檔案的不同位置插入資料,對於大型表,這會導致大量隨機物理I/O。這樣會大大降低操作的效果。