標籤:sql查詢 應該 尋找 位元組 插入 sel 索引值 影響 計劃
1.1 叢集索引
表的資料是儲存在資料頁中(資料頁的PageType標記為1),SqlServer一頁是8k,存滿一頁就開闢下一頁儲存。如果表有叢集索引,那麼一筆一筆物理資料就是按叢集索引欄位的大小升/降排序儲存在頁中。當對叢集索引欄位更新或中間插入/刪除資料時,都會導致表資料移動(造成效能一定影響),因為它要保持升/降排序。
注意,主鍵只是預設是叢集索引,它也可以設定為非叢集索引,也可以在非主鍵欄位上設定為叢集索引,全表只能有一個叢集索引。
一個優秀的叢集索引欄位一般包含以下4個特性:
(A).自增長
總是在末尾增加記錄,減少分頁和索引片段。
(B).不被更改
減少資料移動。
(C).唯一性
唯一性是任何索引最理想的特性,可以明確索引索引值在排序中的位置。
更重要的是,索引鍵指唯一的話,它在每條記錄裡才可以正確指向來源資料行RID。如果叢集索引索引值不唯一,SqlServer就需要內部產生uniquifier 列組合當作聚集鍵保證“索引值”唯一性;如果非叢集索引索引值不唯一,就會增加RID列(叢集索引鍵或者堆表中的行指標)保證“索引值”唯一性。
思考(可略過):索引“索引值”在非葉子節點也有保證唯一性,原因應該是為了明確索引記錄在非葉子節點中的位置。比如有個非叢集索引欄位Name2,表中有很多Name2=‘a‘的記錄,導致Name2=‘a‘在非葉子節點上有多條索引記錄(節點),這時候再insert一筆Name2=‘a‘的記錄時,就可以根據非葉子節點的RID和新增記錄的RID很快確定要insert到哪個索引記錄(節點)上,如果沒有非葉子節點的RID,那得遍曆到所有Name2=‘a‘的葉子節點才能確定位置。另外,當我們select * from Table1 where Name2<=‘a‘時,返回的資料是按非叢集索引Name2和RID排序的,很好理解返回的資料就是按這邊索引儲存的順序排序的。這是這條sql查詢時有用到Name2索引的結果,如果資料庫查詢計劃因“臨界點”問題選擇直接表資料掃描,那返回的資料預設就是按表資料的順序排序的。
為了“索引值”唯一性,對於叢集索引,uniquifier 列只在索引值重複時增加。對於非叢集索引,如果建立索引時沒定義唯一,RID會在所有記錄增加,就算索引值是唯一的;如果建立索引時定義唯一,RID只在葉子層增加,用於尋找來源資料行,即書籤尋找操作。
(D).欄位長度小
叢集索引鍵長度越小,一頁索引頁就可以容納更多索引記錄,進而減少索引B樹結構的深度。例如,一個百萬記錄的表有一個int叢集索引,可能只需要3層的B樹結構。如果把叢集索引定義在更寬的列(比如uniqueidentifier列需要16 位元組),那麼索引的深度會增加到4層。任何叢集索引尋找需要4個I/O操作(確切的說是4個邏輯讀),原先只要3個I/O操作。
同樣,非叢集索引裡會包含叢集索引索引值,叢集索引鍵長度越小非叢集索引記錄也就越小,一頁索引頁就可以容納更多索引記錄。
金萬城平台SQL Server索引的原理深入解析扣892118