1. 程式人生 > >sql server 索引總結三

sql server 索引總結三

tro p s uniq phy 子節點 重復 得出 product 提高

一、非聚集索引維護

  非聚集索引的行定位器值保持相同的聚集索引值,即使該聚集索引列物理上重新定位後,也是如此。

  為了優化這個維護開銷,SQL Server添加一個指向舊數據頁的指針,以在頁面分割之後指向新的數據頁面,而不是更新所有相關非聚集索引的行定位器。這樣,雖然降低了非聚集索引的維護開銷,但是增加了從非聚集索引行到數據行的導航開銷,因為添加了一個舊數據頁面和信數據頁面之間的連接。因此,將聚集索引作為行定位器降低了非聚集索引相關的開銷。

二、定義書簽查找

  當一個查詢請求不是優化器選擇的非聚集索引一部分的列時,需要一個查找。這對一個聚集索引來說是一個關鍵字查找,堆堆表來說是一個RID查找。這些查找的統稱來自於舊的定義名 - 書簽查找。這種查找根據非聚集索引的行定位器值,從表中讀取對應的數據行,除了索引頁面上的邏輯讀操作意外,還需要一個數據頁面上的邏輯讀。但是,如果查詢需要的列在索引中,那麽就不需要訪問數據頁面。這被稱為覆蓋索引。

  這些書簽查找是大結果集最好使用聚集索引的原因。聚集索引不需要書簽查找,因為葉子頁面就是數據頁面。

三、非聚集索引的建議

  因為表只能有一個聚集索引,所以可以使用多個非聚集索引的靈活性來幫助改進性能。下面將說明非聚集索引使用的決定因素。

  1、何時使用非聚集索引

  非聚集索引在需要從一個大表上讀取少量的行的時候最有用,隨著需要檢索的行數量的增加,書簽查找的開銷成比例增加。為了從表中檢索少量的行,索引列應該有很高的選擇性。

  適合使用非聚集索引的情況:

  • 列具有高選擇性;
  • 根據列獲取少量數據;
  • 窄列;
  • 列經常被分組排序;

  下面給出不適合建立聚集索引,但也能夠使用聚集索引的情況

  •   頻繁更新的列;
  •   寬類型列;

  聚集索引頻繁更新是非常消耗資源的,因為會影響表的順序,同時也影響到其他索引,在頻繁更新的列上的非聚集索引的開銷不像聚集索引那麽大。在非聚集索引上的更新操作被限定在基本表和非聚集索引上,它不影響表上的其他非聚集索引。

  寬類型列也類似,非聚集索引列上使用寬類型,雖影響比如聚集索引那麽大,但是也要小心使用。

  2、何時不使用聚集索引

  非聚集索引不適合於大量行的查詢。這樣的查詢使用聚集索引更好,聚集索引不需要大量的書簽查找,而非聚集索除了在索引列上檢索的邏輯讀外,書簽查找也需要消耗太多資源。SQL Server查詢優化器在檢索大結果集時會考慮這一開銷,並相應地放棄該非聚集索引。

不適合建非聚集索引的情況: 獲取大量數據; 低選擇性;

聚集索引、非聚集索引、非聚集唯一索引

  我們都知道建立適當的索引能夠提高查詢速度,優化查詢。先說明一下,無論是聚集索引還是非聚集索引都是B樹結構

  •   聚集索引默認與主鍵相匹配,在設置主鍵時,SQL Server會默認在主鍵列創建聚集索引。但是可以手動更改為在任意一個列創建聚集索引,然後在另一個字段或多個字段上定義主鍵。這時主鍵將會被作為一個唯一的非聚集索引(唯一索引)被創建。通過指定NONCLUSTERED關鍵字就可以做到。
    CREATE TABLE MyTableKeyExample
    {
      Column1 int IDENTITY  KEY NONCLUSTERED,
      Column2 int 
    }
  •   聚集索引實際上裝載了SQL Server的數據記錄行,聚集索引的葉級就是數據行,所以到達了聚集索引的葉級就到達了數據。
  •   為表聲明主鍵或唯一約束時,SQL Server會自動創建與之對應的唯一索引。
  •   聚集索引的列最好就是自增的,因為根據區段-頁的理論,如果聚集索引列是自增的,那麽添加數據的時候,所見是放在索引的最後,不會發生由中間插入的情況,這樣就不會引起頁拆分。而如果索引列不是自增的,添加數據的時候,還要按順序找到該條記錄的位置,並且插入,如果插入比較頻繁,還可能會經常引起頁拆分。
  •   聚集索引的最佳數據類型,smallint、int、bigint、datetme。
  •   最好避免組合聚集索引。

  索引(index)是除了表之外的另一個重要的、用戶定義的存儲在數據庫裏的數據結構。當根據索引碼的值搜索數據時,索引提供了對數據的快速訪問。事實上,沒有索引數據庫也能夠根據SELECT語句通過表掃描成功地檢索到結果,但是隨著表變得越來越大,使用“適當”的索引的效果就越來越明顯。但如果使用索引時不認真考慮其實現過程,索引反而有可能會降低數據庫的工作性能。創建主鍵時會自動創建聚集索引,除非當前表中已經含有了聚集索引或是創建主鍵時指定了NONCLUSTERED關鍵字。

  聚集索引、非聚集索引、非聚集唯一索引:

  SqlServer提供了兩種索引:聚集索引和非聚集索引。聚集的作用就是將某一列(或是多列)的物理順序改變為和邏輯順序相一致。

  聚集索引(CLUSTERED)與非聚集索引(NONCLUSTERED)的區別:

  其實,我們的漢語字典的正文本身就是一個聚集索引(按照拼音的英文字母排序) 比如,我們要查“安”字 ,就會很自然地翻開字典的前幾頁,因為“安”的拼音是“an”,而按照拼音排序漢字的字典是以英文字 母“a”開頭並以“z”結尾的,那麽“安”字就自然地排在字典的前部。如果您翻完了所有以“a”開頭的部分仍然找不到這個字,那麽就說明您的字典中沒有這個字;同樣的,如果查“張”字,那您也會將您的 字典翻到最後部分,因為“張”的拼音是“zhang”。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。我們把這種正文內容本身就是一種按照一定規則排列的目錄稱為“聚集索引”。 這也是為什麽一張表只能 夠有一個聚集索引的原因。因為一張表只能夠按一種方式排序。 其中聚集索引的葉級節點就是數據。你可以理解為有很多列火車,按照火車頭索引(排序),火車頭後面跟著的就是數據。   如果您認識某個字,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛才的方法找到您要查的字,而需要去根據“偏旁部首”查到您要找的字,然後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合“部首目錄”和“檢字表”而查到的字的排序並不是真正的正文的排序方法,比如您查“張”字,我們可以看到在查部首之後的檢字表中“張” 的頁碼是672頁,檢字表中“張”的上面是“馳”字,但頁碼卻是63頁,“張”的下面是 “弩”字,頁面390頁。很顯然,這些字並不是真正的分別位於“張”字的上下方,現在您看到的連續的“馳、張、弩” 三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然後再翻到您所需要的頁碼。我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱為“非聚集索引”。

  實際上如果表上有聚集索引,則非聚集索引中存儲著聚集索引的引用,然後通過利用聚集索引來獲取數據。因此這就是為什麽返回少量數據的時候使用非聚集索引的性能較好,而返回大量的數據的時候使用非聚集索引還不如直接全表掃描來得快。而如果表上沒有聚集索引的時候,則引用行號。

  非聚集索引需要額外的空間進行存儲,按照被索引列進行聚集索引,並在B樹的葉子節點包含指向非聚集索引所在表的指針。非聚集索引也是B樹結構,另外一個單獨的B樹。

  唯一索引:

  當主鍵創建時如果不設置為聚集索引,那麽就一定是唯一的非聚集索引。實際上,唯一索引,故名思議就是它要求該列上的值是唯一的。

  聚集索引列可以重復,非聚集索引列也可以重復。這就是為什麽要有唯一這個東東了。聲明唯一索引的語法很簡單,只是多了個UNIQUE關鍵字。

  CREATE UNIQUE NONCLUSTERED INDEX [AK_Product_Name] ON Production.Product ( [Name] );

  唯一索引有很多限制和特性。下面詳細學習下唯一索引。

  為表聲明主鍵或唯一約束時,SQL Server會自動創建與之對應的唯一索引。定義一個唯一約束時,SQL Server會自動創建一個與之同名的唯一索引,要刪除索引必須先刪除約束。但刪除約束,刪除約束也會導致與之關聯的索引被刪除,也就是說,不能刪除唯一索引,要刪除索引只有刪除唯一約束這個辦法。

  唯一索引依賴於唯一約束,刪除唯一索引必須刪除唯一約束。另外SQL Server又在建立唯一約束時又默認建立唯一索引。

  總結起來就是:唯一索引與唯一約束始終一同存在。

  因為定義一個主鍵或是定義約束會導致索引被創建,所以你必須在約束定義時就給出必要的索引信息,因此上面ALTER TABLE語句中包含了”CLUSTERED”關鍵字。

ALTER TABLE Production.Product ADD CONSTRAINT PK_Product_ProductID PRIMARY KEY 
CLUSTERED ( ProductID );

   如果唯一索引或約束所約束的列在當前的表中已經含有了重復值,那麽創建索引會失敗。而當唯一索引創建成功後,所有違反這個約束的INSERT、UPDATE語句都會失敗。

消息 2601,級別 14,狀態 1,第 1 行 
不能在具有唯一索引 ‘AK_Product_Name‘ 的對象 
‘Production.Product‘ 中插入重復鍵的行。 
語句已終止。 

  唯一約束和唯一索引並沒有顯著的區別。創建獨立的唯一索引和使用唯一約束對於數據的驗證方式並無區別。查詢優化器也不會區分唯一索引是由約束創建還是手工創建。然而以數據完整性為目標的話,最好創建約束,這使得對應的索引的目標一目了然。

  過濾唯一索引,當我們需要既允許多個NULL值,又不允許重復的時候,可以使用這個:

  CREATE UNIQUE NONCLUSTERED INDEX xx on 
  ProductDemo(<索引列>)  --指定索引列
  where <索引列>!=null)  --過濾條件

  對於用以上語法創建的唯一索引,插入時,只有當唯一索引列不為NULL的時候才檢測重復。換句話說,以上表的索引列允許多個NULL值。

一、索引視圖基本概念

  索引視圖實際上是一種將一組唯一值“物化”為群集索引形式的視圖,所為物化就是幾乎和表一樣,其數據也是會存儲一份的(會占用硬盤空間,但是查詢速度快,例如可以將count(),sum()等值設在索引視圖中)。其優點是它在提取視圖背後的信息方面提供了一個非常快的查找方法。在第一個索引(必須是針對一組唯一值的聚集索引)之後,通過使用來自第一個索引的聚集鍵作為參考點,SQL Server還能在視圖上建立額外的索引。其限制如下:

  1. 視圖必須使用SCHEMABINDING選項;
  2. 如果視圖引用了任何用戶自定義函數,那麽這些函數也必須是模式綁定的;
  3. 視圖不可以引用任何其他的視圖-只能引用表和UDF;
  4. 在視圖中引用的所有表和UDF必須采用兩部分的命名約定(例如:dbo..Customers),並且也必須具有和視圖相同的所有者;
  5. 視圖和視圖引用的所有對象必須在相同的數據庫中;
  6. 在創建視圖和所有底層表時,必須打開ANSI_NULLS以及QUOTED_IDENTIFIER選項;
  7. 視圖引用的任何函數必須是確定的;

  示例:

  CREATE VIEW CustomerOrders_vw
  WITH SCHEMABINDING
  AS
  SELECT ....

  當創建索引時,在視圖上創建的第一個索引必須是聚集的和唯一的:

  CREATE UNIQUE CLUSTERED INDEX ivCustomerOrders
  ON CustomerOrders_vw(AccountNumber,SalesOrderID,ProductID)

  一旦執行該命令,就有了視圖的群集索引。索引基本和表的一樣,也需要維護成本。

二、索引視圖作用示例

  PersonTenMillion是一張一千萬記錄的表,下面我們來執行如下SQL語句:

SELECT Age,COUNT(Age) FROM PersonTenMillion
GROUP BY Age
ORDER BY Age

  對一張1千萬記錄的表進行分組計算每個年齡的認輸,你可以想象到需要花費的時間了。

  技術分享圖片

  1分31秒,這種查詢語句如果在網頁上面,頁面已經顯示頁面無法響應了。

  下面我們來優化上面這個查詢,我們創建一個索引視圖如下:

技術分享圖片
--創建模式綁定視圖
CREATE VIEW PersonAge_vw
WITH SCHEMABINDING
AS
SELECT Age,COUNT_BIG(*) AS CountAge FROM dbo.PersonTenMillion
GROUP BY Age

--為視圖創建索引
CREATE UNIQUE CLUSTERED INDEX ivPersonAge
ON PersonAge_vw(Age)
技術分享圖片

  這次我們從索引視圖上獲取數據:

SELECT * FROM PersonAge_vw

  這次是瞬間出來的,因為只是相當於從一個81行的表中使用聚集索引分那會81行數據:

  技術分享圖片

  技術分享圖片

  查詢速度快了好多好多,但這以為這索引視圖是好的選擇嗎?不是的,這只意味著它可能是。和任何索引一樣,需要記住索引的維護成本。維護該索引將會使對底層表的INSERT、UPDATE和DELETE語句的執行速度減慢多少?這必須考慮進去,這是個平衡問題,要視每個表和每個索引而定。盡管如此,索引視圖還是一種較強大的工具,因此作仔細地權衡。

一、索引壓縮

  數據和索引壓縮在SQL Server2008被引入。壓縮一個索引意味著將在一個頁面中獲得更多的關鍵字信息。這可以造成重大的性能改進,因為存儲索引需要的頁面和索引級別更少。因為索引中的鍵值被壓縮和解壓縮,也將造成CPU和內存的開銷,所以這並不是適合所有索引的方案。

  默認情況下,索引將不會被壓縮。必須明確地在創建索引時要求索引被壓縮。有兩種壓縮類型:行級壓縮和頁面級壓縮。索引中的非葉子頁面不接受頁面類型壓縮。

  創建壓縮索引的語法如下:

CREATE NONCLUSTERED INDEX IX_Person_Name
ON PersonOneMillion(Name)
WITH(DATA_COMPRESSION = Page)

  下面以一個示例來看看壓縮索引的作用:

  正常索引:

  技術分享圖片

  行級壓縮索引:

  技術分享圖片

  頁面級壓縮索引:

  技術分享圖片

  我們看到對於100萬索引的Name列索引,索引數據頁面分別是:

普通索引 行索引 頁面索引
3109 2135 1962

  從上面的例子我們可以得出結論,的確壓縮能夠能夠大幅減少索引的頁面量。但是舉個很簡單的例子,如果一臺數據庫服務器上,內存和CPU已是瓶頸,那麽壓縮數據作用實際上並不大。

  至於更具體的哪個更好,這個有時間了再慢慢測試。現在只是知道了索引可以壓縮已減少索引數據頁面數量。

  附上一個查看索引消息的SQL語句:

技術分享圖片
--查看索引數據頁數
SELECT i.name,i.type_desc,s.page_count,s.record_count,s.index_level,compressed_page_count
FROM sys.indexes i JOIN sys.dm_db_index_physical_stats(DB_ID(N‘DataExample‘),OBJECT_ID(N‘PersonOneMillion‘),NULL,NULL,‘DETAILED‘) AS s
ON i.index_id = s.index_id
WHERE i.OBJECT_ID = OBJECT_ID(N‘PersonOneMillion‘)
技術分享圖片

二、 附加特性

  1、不同的列排序順序

  SQL Server支持使用不同的排序順序為索引的不同列創建一個復雜的索引。如果希望一個索引的第一列按照升序排列二第二列按照降序排列,可以用如下語句完成:

  CREATE NONCLUSTERED INDEX IX ON Table(c1 ASC,c2 DESC)

  2、BIT數據類型列上的索引

  SQL Server允許創建在BIT數據類型列上的索引。創建BIT數據類型列上的索引的能力本身不是一個大的優點,因為這樣的列只能有兩個不同的值。這麽低的選擇性的列通常不是好的索引後選擇。但是,這個功能在考慮覆蓋索引時非常有用。因為覆蓋索引需要包含所有搜索中的返回列,而在索引中添加BIT數據類型列將使得覆蓋索引在需要時包含這樣的列。

  3、CREATE INDEX語句也會使用索引提升速度

  CREATE INDEX操作被集成到查詢處理器。優化器可能使用已有的索引來減少掃描開銷並在創建索引時排序。

  技術分享圖片

  在第一個索引中由於已經包含了Name列,而第二個索引也要使用Name列的時候,創建索引SQL Server直接使用索引掃描的方式來創建索引。

  4、並行索引創建

  SQL Server支持CREATE INDEX語句的並行計劃,正如在其他SQL查詢中一樣。在一個多處理器的機器上,索引創建不限於單個處理器而是從多個處理器中獲益。可以使用SQL Server的max degree of parallelism配置參數來控制用於CREATE INDEX語句中的處理器數量。這個參數的默認值為0,0表示可以使用任意的CPU數量。

EXEC sp_configure ‘max degree of parallelism‘     --默認值為0

EXEC sp_configure ‘max degree of parallelism‘, 2    --使用2個CPU
RECONFIGURE WITH OVERRIDE

  這個配置設置立即生效,不需要重啟服務器。   查詢提示MAXDOP可以用於CREATE INDEX語句。而且,CREATE INDEX特性只可以用於SQL Server 2005和2008企業版。

一、書簽查找的概念

  書簽可以幫助SQL Server快速從非聚集索引條目導向到對應的行,其實這東西幾句話我就能說明白。

  如果表有聚集索引(區段結構),那麽書簽就是從非聚集索引找到聚集索引後,利用聚集索引定位到數據。此處的書簽就是聚集索引。如果表沒有聚集索引(堆結構)。那麽掃描非聚集索引後,通過RID定位到數據,那麽此處書簽就是RID。

  所謂的書簽查找,就是通過聚集索引,然後利用聚集索引或RID定位到數據。

  不論表示堆結構還是區段結構,數據的存放都是數據庫文件的某文件->某頁->某行,因此定位數據的文件組合起來就是 文件號:頁號:行號。這三個數字就是RID。如文件1的第77頁的第12行的RID就是1:77:12。

  堆結構與區段結構不同,通常堆上的行不會改變位置,一旦他們被插入某個頁中,他們就會一直在那個位置。在堆上的行很少移動,如果行被移動的話,他們會在原來的位置留下指向其移動到的新位置的指針。而區段結構的行,是可以移動的,在添加數據或整理索引時,都可以會被移動位置。

  因為在堆上的行很少移動,所以RID就可以唯一標識某一行,RID的值不僅僅不變,RID所表示的行的物理位置也不會變,這使得RID的值更適宜作為書簽。這也是為什麽SQL Server在堆上建立的非聚集索引的書簽都使用RID。

  1、堆上的非聚集索引:基於RID的書簽

CREATE NONCLUSTERED INDEX FK_ProductID_ModifiedDate   --主鍵不是聚集索引,沒有聚集索引
ON 
Sales.SalesOrderDetail(ProductID, ModifiedDate) 
INCLUDE (OrderQty, 
UnitPrice, LineTotal) 

  部分數據順序:

  技術分享圖片

  註意到以上數據是無序的。

  上面建立的非聚集索引因為使用了RID作為書簽,直接指向對應行所在的物理位置,因此效率不錯。雖然RID值用於鍵查找非常高效,但書簽中包含的值與具體的用戶數據無關。

  2、在聚集索引上的非聚集索引:基於聚集鍵的書簽

  如果表示基於聚集索引的,則表內數據可以在表移動。因此,對於聚集索引來說,RID並不能一直不變的定位一個相同的行。因此必須用另外的方法定位行,這個方法就是使用聚集索引的索引鍵。   使用聚集索引鍵作為書簽可以使得當數據在頁中的行改變時,不需要非聚集索引的書簽的值進行變動,因此非聚集索引的鍵就可以用於去找底層表的數據,即根據書簽取數據不再基於物理位置,而是基於聚集索引查找。

  技術分享圖片   以聚集索引鍵作為非聚集索引的書簽最好要聚集索引鍵滿足如下標準:   索引應該具有唯一性:每一個索引條目書簽都應該使得書簽可以通過聚集索引的鍵值唯一的確認表中的一行,如果你創建的聚集索引鍵值不唯一,SQL Server將會為有重復鍵值的每一行自動加上一個叫uniquifier的東西使得每一行唯一。這個uniquifier對客戶端是透明的。對於是否可以允許聚集索引鍵重復,要考慮以下兩點:

  •   生成uniquifier增加SQL Server插入操作的額外負擔,在插入時SQL Server還需要判斷插入的值在表中是否唯一,如果不唯一生成uniquifier值再進行插入。
  •   uniquifier本身對業務數據來說是沒有意義的,但是這個uniquifier本身不僅僅需要占用聚集索引鍵的空間,還同時占用非聚集索引書簽的空間

  索引鍵應該短:索引鍵所占的字節數應該短.因為這個鍵還會占用非聚集索引書簽的空間。比如Contact表中以Last name / first name / middle name / street組合作為索引鍵看上去不錯,但如果表中存在多個非聚集索引的話情況就有些微妙了。n個非聚集索引使得Last name / first name / middle name / street這些字段被存儲在n+1個位置。

  索引鍵最好不要變動:也就是索引鍵的值最好不要變動。對於聚集索引鍵的修改會使得基於這個聚集索引的所有非聚集索引同樣進行修改。所以對於聚集索引的一次update會造成n個非聚集索引書簽的update+1個聚集索引鍵值本身的update。

  下面以一個示例來幫助理解書簽查找:

  假設數據庫有一張表如下:

  技術分享圖片

  我們再Name列建一個非聚集索引,然後執行下面的語句:

  技術分享圖片

  從執行計劃我們可以看到,因為Age列並不在非聚集索引中,所以SQL Server通過“鍵查找”引導到聚集表獲取數據,這就是書簽查找。

  書簽查找的目的,就是為了從非聚集索引導航到基本表獲取非聚集索引中並未包含的信息。

二、書簽查找的缺點

  書簽查找要求訪問索引頁面之外的數據頁面,訪問兩組頁面增加了查詢邏輯讀操作次數。而且,如果頁面不在內存中,書簽查找可能需要在磁盤上一個隨機I/O操作來從索引頁面跳轉到數據頁面,還需要必要的CPU能力來匯集這一數據並執行必要的操作。這是因為對於大的表,索引頁面和對應的數據頁面通常在磁盤上並不臨近。

  如果需要增加邏輯讀操作或者開銷較大的物理讀操作使書簽查找的數據檢索操作開銷相當大,這個開銷因素是非聚集索引更適合於返回較小的數據行數的原因。隨著查詢檢索的行數增加,書簽查找的開銷將變得無法接受。

  為了理解書簽查找隨著檢索行數增加而使feu聚集索引無效,下面來看一個實例:

  還是那張Person表,一萬數據。這次,我把索引建在Id列,Id列的唯一性是1,因為原來Id列是做主鍵+聚集索引的,但被我刪掉了。

  我們來看看下面兩個查詢的執行計劃,

  返回100條:

  技術分享圖片

  返回300條:

  技術分享圖片

  我們看到,當要求返回300條數據的時候,SQL Server就不在使用Id列上的非聚集索引,而是直接進行表掃描了。因為SQL Server認為執行300次書簽查找還不如直接對一張1萬條記錄的表進行全表掃描。

  由上面的實例可以得出結論,返回大的結果集將增加書簽查找的開銷,甚至低於表掃描。因此在返回較大結果集的情況下,必須考慮避免書簽查找的可能性。

三、書簽查找的起因

  書簽查找可能是一個開銷較大的操作,所以應該分析查詢計劃,在執行計劃中選擇一個關鍵字查找步驟的原因。可能發現可以通過在非聚集索引鍵中包含丟失的行,或者作為索引頁面級別上的包含列來避免書簽查找,從而避免與書簽查找相關的開銷。

  從上面的實例,我們可以提出觀點:如果查詢的各部分(不只是選擇列表)中引用的列不都包含在使用的非聚集索引中,就會發生書簽查找操作。

  下面介紹一個技巧,我們點擊某一個執行計劃的圖標之後,就能在右側的屬性信息欄裏獲取到相關的執行信息。例如,輸出列表就是本執行計劃的要返回的列。

  技術分享圖片

四、避免書簽查找的方法

  因為書簽查找的相對開銷可能非常高,所以應該盡可能嘗試擺脫書簽查找操作。下面給出一下方案。

  1、使用聚集索引

  對於聚集索引,索引的葉子頁面和表的數據頁面相同。因此,當讀取聚集索引鍵列的值時,數據引擎可以讀取其他列的值而不需要任何導航。例如前面的區間數據查詢的操作,SQL Server通過B樹結構進行查找是非常快速的。

  把非聚集索引轉換為一個聚集索引說起來很簡單。但是,這個例子和大部分可能遇到的情況下,這不可能做到,因為表已經有了一個聚集索引。這個表的聚集索引恰好是主鍵。必須卸載掉所有的外鍵約束,卸載並且重建為一個非聚集索引。這不僅要考慮所涉及的工作,還可能嚴重地影響依賴於現有聚集索引的其他查詢。

  2、使用覆蓋索引

  為了理解覆蓋索引是如何避免書簽查找,我們還是對於Person來執行如下兩個查詢:

  技術分享圖片

  下面修改索引增加Name列。

  技術分享圖片

  由於非聚集索引上已經有了需要查詢的Id和Name列的數據,所以不在需要書簽查找定位到基本表。

  3、使用索引連接

  如果覆蓋索引變得非常寬,那麽可能要考慮索引連接技術。索引連接技術使用兩個或更多索引之間的一個索引交叉來完全覆蓋一個查詢。因為索引連接技術需要訪問多余一個索引,它必須在所有索引連接中使用的索引上執行邏輯讀。因此,索引連接需要比覆蓋索引更高的邏輯讀數量。但是,因為索引連接所用的多個窄索引能夠比寬的覆蓋索引服務更多的查詢。所以索引連接也可以作為避免書簽查找的一種技術來考慮。

  我們來看下面的實例:

  技術分享圖片

  留意到,上面的例子我們創建了兩個非聚集索引,一個在 Id列,一個在Name列。但是我們的查詢需要同時返回Id列和Name列。而這兩個非聚集索引都不完全包含要返回列。這個時候,哈希匹配目的就是通過定位到索引,而不用定位到基本表就能夠獲得我們所需要的全部數據,這樣索引連接就避免了書簽查找。

一、索引的圖形界面操作

  SQL Server非常強大的就是圖形界面操作。關於索引方面也一樣那麽強大,很多操作比如說重建索引啊,查看各種統計信息啊,都能夠通過圖形界面快速查看和操作,下面來看看SQL Server索引方面的GUI操作。

  技術分享圖片

  技術分享圖片

  技術分享圖片

  技術分享圖片

  技術分享圖片

  技術分享圖片

  技術分享圖片

  技術分享圖片

  技術分享圖片

二、索引統計信息的圖形界面操作

  在有大量事務的數據庫中,表和索引隨著時間的推移而碎片化。因此,為了增進性能,應該定期檢查表和索引的碎片,並對具有大量碎片的進行整理。

  1、確定當前數據庫中所有需要分析碎片的表。

  2、確定所有表和索引的碎片。

  3、考慮一下因素以確定需要進行碎片整理的表和索引。

  •   高的碎片水平-avg_fragmentation_in_percent大於20%;
  •   不是非常小的表或索引-也就是page_count大於8的;

  4、整理具有大量碎片的表和索引;

  這裏給出一個樣板SQL存儲過程,它執行以下操作;

  •   遍歷系統上的所有數據庫並確認符合碎片條件的每個數據庫中表上的索引,並將它們保存到一個臨時表中;
  •   根據碎片水平,重新整理碎片較少的索引並重建碎片很多的索引。
技術分享圖片
CREATE PROCEDURE IndexDefrag
AS

DECLARE @DBName NVARCHAR(255)
    ,@TableName NVARCHAR(255)
    ,@SchemaName NVARCHAR(255)
    ,@IndexName NVARCHAR(255)
    ,@PctFrag DECIMAL

DECLARE @Defrag NVARCHAR(MAX)

IF EXISTS (SELECT * FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N‘#Frag‘))
    DROP TABLE #Frag

CREATE TABLE #Frag
(DBName NVARCHAR(255)
,TableName NVARCHAR(255)
,SchemaName NVARCHAR(255)
,IndexName NVARCHAR(255)
,AvgFragment DECIMAL)

EXEC sp_msforeachdb ‘INSERT INTO #Frag (
    DBName,
    TableName,
    SchemaName,
    IndexName,
    AvgFragment
) SELECT  ‘‘?‘‘ AS DBName
       ,t.Name AS TableName
       ,sc.Name AS SchemaName
       ,i.name AS IndexName
       ,s.avg_fragmentation_in_percent 
FROM    ?.sys.dm_db_index_physical_stats(DB_ID(‘‘?‘‘), NULL, NULL,
                                       NULL, ‘‘Sampled‘‘) AS s
        JOIN ?.sys.indexes i
        ON s.Object_Id = i.Object_id
           AND s.Index_id = i.Index_id
        JOIN ?.sys.tables t
        ON i.Object_id = t.Object_Id
        JOIN ?.sys.schemas sc
        ON t.schema_id = sc.SCHEMA_ID
WHERE s.avg_fragmentation_in_percent > 20
AND t.TYPE = ‘‘U‘‘
AND s.page_count > 8
ORDER BY TableName,IndexName‘

DECLARE cList CURSOR
FOR SELECT * FROM #Frag

OPEN cList
FETCH NEXT FROM cList
INTO @DBName, @TableName,@SchemaName,@IndexName,@PctFrag
WHILE @@FETCH_STATUS = 0
BEGIN
    IF @PctFrag BETWEEN 20.0 AND 40.0
    BEGIN
        SET @Defrag = N‘ALTER INDEX ‘ + @IndexName + ‘ ON ‘ + @DBName + ‘.‘ + @SchemaName + ‘.‘ + @TableName + ‘ REORGANIZE‘
        EXEC sp_executesql @Defrag        
        PRINT ‘Reorganize index: ‘ + @DBName + ‘.‘ + @SchemaName + ‘.‘ + @TableName +‘.‘ + @IndexName
    END
    ELSE IF @PctFrag > 40.0
    BEGIN
        SET @Defrag = N‘ALTER INDEX ‘ + @IndexName + ‘ ON ‘ + @DBName + ‘.‘ + @SchemaName + ‘.‘ + @TableName + ‘ REBUILD‘
        EXEC sp_executesql @Defrag
        PRINT ‘Rebuild index: ‘+ @DBName + ‘.‘ + @SchemaName + ‘.‘ + @TableName +‘.‘ + @IndexName
    END
        
    FETCH NEXT FROM cList
    INTO @DBName, @TableName,@SchemaName,@IndexName,@PctFrag

END
CLOSE cList
DEALLOCATE cList

DROP TABLE #Frag
技術分享圖片

  為了自動化碎片分析過程,可以從SQL Server企業管理器中用以下簡單的步驟創建一個SQL Server任務。

  1、開啟SQL Server代理;

  技術分享圖片

  2、打開Management Studio,右鍵單擊,選擇新建=》任務;

  技術分享圖片

  3、在新建任務對話框的“常規”頁面中,輸入任務名稱和其他細節:

  技術分享圖片

  4、在新建任務對話框的“步驟”頁面中,單擊“新建”並輸入用戶數據庫的SQL命令。

  技術分享圖片

  5、在新建任務步驟對話框“高級”頁面上,輸入報告碎片分析結果的輸出文件名稱:

  技術分享圖片

  6、單擊“確定”按鈕,返回新建作業對話框;

  7、在新建任務對話框“計劃”頁面,單擊“新建計劃”,並輸入運行SQL Server任務的合適計劃:

  技術分享圖片

  安排這個存儲過程在非高峰執行。為了確定數據庫的數據庫模式,記錄整天的SQL Server:SQL Statistics\Batch Requests/sec性能計數器,它將展示數據庫負載的波動。

  8、單擊“確定”按鈕,返回新建任務對話框。

  9、輸入所有信息後,單擊新建任務對話框中的“確定”按鈕創建SQL Server任務。創建計劃在一個固定時間間隔(每周)運行sp_indexDefrag存儲過程的SQL Server任務。

  10、確保SQL Server代理運行,這樣SQL Server任務將自動根據設置的計劃運行。

  這個SQL任務將在每個星期天的淩晨1點分析每個數據庫並且進行碎片整理。

sql server 索引總結三