3 列的聚集索引是否太大?
我的目標是設計一個表,可以通過外部 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
.
SELECT extChartId, valueD, valueN FROM Mytable WHERE companyId = @companyId AND createdBy = @userId
這可以滿足以下指標
(companyId, createdBy) INCLUDE (extChartId, valueD, valueN)
SELECT extChartId, valueD, valueN FROM Mytable WHERE companyId = @companyId AND createdBy = @userId AND dashboardId = @dashboardId
這可以滿足以下指標
(companyId, createdBy, dashboardId) INCLUDE (extChartId, valueD, valueN)
SELECT extChartId, valueD, valueN FROM Mytable WHERE dashboardId IN (@0, @1, @2)
這可以滿足以下指標
(dashboardId) INCLUDE (extChartId, valueD, valueN)
SELECT extChartId, valueD, valueN FROM Mytable WHERE (companyId = @companyId AND createdBy = @userId AND dashboardId = @dashboardId) OR dashboardId IN (@0, @1, @2)
這個比較困難,需要一個索引聯合(可能需要重寫查詢才能得到它)。所需的索引將與 #1 和 #3 相同
- 我們把這個改成只使用自然鍵,所以和#6一模一樣
UPDATE Mytable SET valueD = @valueD WHERE companyId = @companyId AND createdBy = @userId AND extChartId = @extId
因為
extChartId
是唯一的,其他列都可以進去,INCLUDE
所以需要一個索引
(extChartId) INCLUDE (companyId, createdBy, valueD)
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
本身是獨一無二的,正如您正確指出的那樣,由於尺寸的原因,使用它可能更有意義。但是死鎖也可能是一個問題,這取決於您的事務更新等的複雜性。從那個開始,如果您發現這是一個問題,請切換集群密鑰。