Sql-Server

3 列的聚集索引是否太大?

  • June 27, 2022

我的目標是設計一個表,可以通過外部 id( uniqueidentifier)、內部 id( bigint) 進行查詢,始終與和/或與 ( , n=0,10) 結合使用companyId(bigint),這兩個條件都起到所有權檢查的作用。userId(bigint)``dashboardId(bigint)``dashboardId IN @0, ..., @n

我想出了以下指數組成:

CREATE CLUSTERED INDEX Mytable_createdBy_cix ON Mytable(companyId, createdBy, dashboardId)

CREATE UNIQUE NONCLUSTERED INDEX Mytable_extId_nix ON Mytable(extId) INCLUDE (valueD, valueN)

CREATE UNIQUE NONCLUSTERED INDEX Mytable_chartId_nix ON Mytable (chartId) INCLUDE (valueD, valueN)

我不知道以下問題的答案:

  • 聚集索引是否因為非唯一而壞?我應該添加隔離鍵而不使用自動分配的uniqueifier嗎?
  • 3 * 8 字節列 + 4 字節唯一符(總共 28 字節)對於聚集索引來說太多了嗎?我讀到它包含在每個唯一非聚集索引的包含頁面中(根據使用的鍵添加額外的 16 或 8 個字節)。
  • 這種索引設計對下面的查詢是否有意義?

我計劃執行查詢,類似於:

SELECT chartId, valueD, valueN FROM Mytable WHERE companyId = @companyId AND createdBy = @userId

SELECT chartId, valueD, valueN FROM Mytable WHERE companyId = @companyId AND createdBy = @userId AND dashboardId = @dashboardId

SELECT chartId, valueD, valueN FROM Mytable WHERE dashboardId IN (@0, @1, @2)

SELECT chartId, valueD, valueN FROM Mytable WHERE (companyId = @companyId AND createdBy = @userId AND dashboardId = @dashboardId) OR dashboardId IN (@0, @1, @2)

UPDATE Mytable SET valueD = @valueD WHERE companyId = @companyId AND createdBy = @userId AND chartId = @chartId

UPDATE Mytable SET valueD = @valueD WHERE companyId = @companyId AND createdBy = @userId AND extChartId= @extId

UPDATE Mytable SET valueD = @valueD WHERE ((companyId = @companyId AND createdBy = @userId) OR dashboardId IN (@0, @1, @2)) AND extChartId= @extId

確實知道,最好在 stackexchange 上提問時測試、評估執行計劃並分享它們,但這是設計階段,因此還沒有實際的數據或表格。

我可以調整鍵/索引/表結構以更好地適應查詢。我只是希望在第一次創建它們時至少部分正確,所以這個問題不會被重新討論。

非常感謝您提前提供的任何幫助。

首先,請注意,僅在實際存在重複值的情況下才添加非唯一鍵上的唯一符。如果同一索引頁面上沒有重複項,則不會佔用任何空間。因此,除非有兩行完全相同,否則companyId, createdBy, dashboardId不會發生這種情況。


寬集群鍵可能會出現問題,但它們也解決了一些死鎖問題,因此這可能是一個因素。無論如何都不清楚您選擇的集群鍵是否正確,但另一方面:UNIQUE考慮到表設計,兩個非集群索引如何有意義?如果它們是唯一的,那麼為什麼這些查詢中所有謂詞都是必需的?

從您的評論看來,額外chartId的只是擁有一個較小的索引列。我認為這可能是一個過早的優化:它只是增加了額外的索引成本,因為您現在還需要索引該列。我建議您刪除它,並完全依賴它,exrChartId即使它更寬。


對於給定的查詢,您需要處理它們並決定如何最好地使用索引來滿足它們。哪個應該是聚群索引的問題有些正交,因為聚群索引有效地INCLUDE自動對所有列。

每個人都可以使用也滿足不同索引的索引,只要前導鍵列相同,而不管鍵或INCLUDE.

  1. SELECT extChartId, valueD, valueN FROM Mytable WHERE companyId = @companyId AND createdBy = @userId

這可以滿足以下指標

(companyId, createdBy) INCLUDE (extChartId, valueD, valueN)


  1. SELECT extChartId, valueD, valueN FROM Mytable WHERE companyId = @companyId AND createdBy = @userId AND dashboardId = @dashboardId

這可以滿足以下指標

(companyId, createdBy, dashboardId) INCLUDE (extChartId, valueD, valueN)


  1. SELECT extChartId, valueD, valueN FROM Mytable WHERE dashboardId IN (@0, @1, @2)

這可以滿足以下指標

(dashboardId) INCLUDE (extChartId, valueD, valueN)


  1. SELECT extChartId, valueD, valueN FROM Mytable WHERE (companyId = @companyId AND createdBy = @userId AND dashboardId = @dashboardId) OR dashboardId IN (@0, @1, @2) 這個比較困難,需要一個索引聯合(可能需要重寫查詢才能得到它)。所需的索引將與 #1 和 #3 相同

  1. 我們把這個改成只使用自然鍵,所以和#6一模一樣

  1. UPDATE Mytable SET valueD = @valueD WHERE companyId = @companyId AND createdBy = @userId AND extChartId = @extId

因為extChartId是唯一的,其他列都可以進去,INCLUDE

所以需要一個索引 (extChartId) INCLUDE (companyId, createdBy, valueD)


  1. UPDATE Mytable SET valueD = @valueD WHERE ((companyId = @companyId AND createdBy = @userId) OR dashboardId IN (@0, @1, @2)) AND extChartId = @extId

同樣,由於OR. 可能有必要將其拆分為兩個單獨的更新。但鑑於這extChartId是獨一無二的,我們可以再次依賴相同的索引。


查看這些指標,我們得出以下結論:

  • 謂詞都是=相等謂詞,或者IN在一個短列表中,因此鍵列可以是任何順序。這極大地幫助了我們組合索引。
  • 適合#1 的索引可以有額外的列來適應#2,但適合#3。同樣適用於#3 的一種也適用於#2,但不適用於#1。所以我們需要單獨的索引。問題仍然是這些組合中的哪些也可以滿足其他查詢。
  • #4 可以使用與前三個相同的索引,所以我們不用擔心。
  • #6 和 #7 需要extChartId,你說這是獨一無二的。因此,所有其餘的列都可以進入,而INCLUDE對性能影響很小。

因此,索引的最佳組合是這樣的

(companyId, createdBy, dashboardId) INCLUDE (extChartId, valueD, valueN)
(dashboardId) INCLUDE (extChartId, valueD, valueN)
(extChartId) INCLUDE (companyId, createdBy, dashboardId, valueD)

問題仍然是您選擇哪一個作為集群鍵。無論您選擇哪一個,INCLUDE所有其他列都會如此。

第一個或第三個索引對我來說最有意義。鑑於它extChartId本身是獨一無二的,正如您正確指出的那樣,由於尺寸的原因,使用它可能更有意義。

但是死鎖也可能是一個問題,這取決於您的事務更新等的複雜性。從那個開始,如果您發現這是一個問題,請切換集群密鑰。

引用自:https://dba.stackexchange.com/questions/313784