標識列上的索引是否應該是非聚集的?
對於具有標識列的表,是否應該為標識列創建聚集或非聚集 PK/唯一索引?
原因是將為查詢創建其他索引。使用非聚集索引(在堆上)並返回索引未覆蓋的列的查詢將使用較少的邏輯 I/O (LIO),因為沒有額外的聚集索引 b-tree 查找步驟?
create table T ( Id int identity(1,1) primary key, -- clustered or non-clustered? (surrogate key, may be used to join another table) A .... -- A, B, C have mixed data type of int, date, varchar, float, money, .... B .... C .... ....) create index ix_A on T (A) create index ix_..... -- Many indexes can be created for queries -- Common query is query on A, B, C, .... select A, B from T where A between @a and @a+5 -- This query will have less LIO if the PK is non-clustered (seek) select A, B, C from T where B between @a and @a+5 ....
身份列上的分群 PK 很好,因為:
- 它單調增加,因此插入時不會拆分頁面。據說批量插入可以像在堆(非聚集)表上一樣快
- 很窄
但是,如果不將其設置為集群,問題中的查詢會更快嗎?
** 更新:** 如果
Id
是其他表的 FK 並且它將在某些查詢中加入怎麼辦?
預設情況下,PK 是集群的,在大多數情況下,這很好。但是,應該問哪個問題:
- 我的 PK 應該集群嗎?
- 哪些列將是我的聚集索引的最佳鍵?
PK 和聚集索引是兩個不同的東西:
- PK 是一個約束。PK 用於唯一標識行,但沒有儲存的概念。但是,預設情況下(在 SSMS 中),如果聚集索引尚不存在,則由唯一聚集索引強制執行。
- 聚集索引是一種特殊類型的索引,它在葉級別儲存行數據,這意味著它始終是覆蓋的。所有列,無論它們是否是鍵的一部分,都儲存在葉級別。它不必是唯一的,在這種情況下,唯一標識符(4 個字節)被添加到集群鍵中。
現在我們有兩個問題:
- 如何唯一標識表中的行 (PK)
- 如何將其儲存在索引的葉級(聚集索引)
這取決於如何:
- 你設計你的數據模型
- 您查詢數據並編寫查詢
- 您插入或更新您的數據
- …
首先,您需要聚集索引嗎?如果批量插入,將無序數據儲存到 HEAP(與集群中的有序數據相比)更有效。它使用 RID(Row Identifier,8 字節)來唯一標識行並將其儲存在頁面上。
聚集索引不應是隨機值。葉級別的數據將按索引鍵儲存和排序。因此它應該不斷增長以避免碎片或頁面拆分。如果 PK 無法做到這一點,則應考慮將另一個密鑰作為集群候選。從順序的角度來看,identy 列、順序 GUID 甚至插入日期之類的聚集索引都很好,因為所有行都將添加到最後一個葉頁。另一方面,雖然唯一標識符作為 PK 可能對您的業務需求有用,但它們不應該集群(它們是隨機排序/生成的)。
如果經過一些數據和查詢分析,你發現在聚集PK中進行key查找之前,你大多使用相同的索引來獲取你的數據,你可以認為它是聚集索引,儘管它可能不能唯一地標識你的數據。
聚集索引鍵由您要索引的所有列組成。如果沒有唯一約束,則添加唯一性列(4 個字節)(重複項的增量值,否則為 null)。然後,該索引鍵將在所有非聚集索引的葉級別為每一行儲存一次。其中一些還將在索引樹(B-tree)的根和葉級別之間的中間級別(分支)儲存多次。如果key太大,所有的非聚集索引都會變大,需要更多的儲存空間和更多的IO、CPU、記憶體……如果你有一個名字+生日+國家的PK,很有可能這個key不是一個好的候選人。它對於聚集索引來說太大了。使用 NEWSEQUENTIALID() 的唯一標識符通常不被視為窄鍵(16 字節),儘管它是順序的。
然後,一旦您弄清楚如何唯一標識表中的行,就可以添加一個 PK。如果您認為不會在查詢中使用它,請不要創建集群。如果您有時需要查詢它,您仍然可以創建另一個非聚集索引。請注意,PK 將自動創建唯一索引。
非聚集索引將始終包含聚集鍵。但是,如果索引列(+鍵列)被覆蓋,則聚集索引中不會有任何鍵查找。不要忘記您還可以將 Include 和 Where 添加到非聚集索引。(明智地使用它)
聚集索引應該是唯一的並且盡可能窄聚集索引不應該隨著時間而改變並且應該增量地插入。
現在是時候編寫一些 SQL 來創建表、聚集和非聚集索引和約束了。
這都是理論上的,因為我們不知道您使用的數據模型和數據類型(A 和 B)。
對於標識列上具有主鍵 (PK) 的表,預設情況下將對其進行分群。非集群會更好嗎?
如果您要詢問標識列(特別是)上主鍵的預設值是否應該是非聚集的,我會說不。大多數表都受益於聚集索引,因此將聚集作為主鍵約束的預設值可能總體上很有幫助,尤其是對於 SQL Server 的新使用者。
與幾乎所有選項一樣,總是存在不同的情況,其中一個優先於另一個,但有經驗的 DBA 應該知道預設值,並能夠在適當的時候覆蓋它。另請參閱相關問答,何時應將主鍵聲明為非聚集的?.
如果不將其設置為集群,問題中的查詢會更快嗎?
是的,但有一些警告。
RID 查找確實比 Key 查找更有效。即使所有需要的頁面都在記憶體中(很可能用於索引的上層),也存在與導航聚集索引 b-tree 相關的 CPU 成本。因此,SQL Server 通常可以執行比每單位 CPU 時間的鍵查找更多的 RID 查找。
注意事項
在決定是否將表建構為堆時,上述內容通常不是決定因素。考慮到硬體環境和工作負載,避免查找(使用覆蓋索引)必須是不切實際的,並且查找的數量必須足夠大,以便對性能產生可衡量的(和重要的)影響。
在這個答案中涵蓋堆與聚集索引辯論的所有方面並不是很實際,但我會說,一般來說,傾向於將表建構為堆的充分理由相對較少。對我來說,選擇問題中提出的那種設計需要在實施前進行非常仔細的分析,並且必須達到很高的標準。關於“可擴展性”的一般論點是不夠的。
關於連接問題的更新,評估失去聚集索引對執行計劃的影響將構成上述分析的一部分。如果使用嵌套循環連接,則在連接鍵上使用聚集索引非常方便,因為該行中的所有列都可以立即使用而無需查找。
我自己的經驗是,在標識列上擁有唯一的聚集索引通常是有益的,所有事情都被考慮在內。我發現堆在空間管理方面存在問題,而且我還應該提到一些 SQL Server 功能需要唯一的聚集索引才能執行。