GUID 上的聚集索引是否比非聚集索引產生更多碎片?
我有一個簡單的表:
CREATE TABLE [dbo].[UserTestGroups]( [UserTestGroupId] [int] IDENTITY(1,1) NOT NULL, [Token] [nvarchar](100) NOT NULL, [TestId] [bigint] NOT NULL, [Group] [tinyint] NOT NULL, [InsertDate] [datetime] NOT NULL)
該表將有相對大量的插入 - 最初 10,000 個會話,每個會話 8 行,每天總共插入 80,000 個。我們預計在不久的將來會顯著增加。無論如何,我們試圖使我們的平台盡可能地具有彈性,而不僅僅是為了適應目前的負載。
該表將用於報告,這可以被視為次要要求。
由於我們使用的是實體框架(微軟的 ORM),所有寫入的表都需要一個邏輯主鍵,所以我添加了一個我簡單地忽略的 Identity 列。我不喜歡使用複合鍵,它們往往會成為 ORM 的噩夢,所以除非我必須這樣做,否則我會添加另一個標識列並將其設為 PK。
所有 BI 報告的查詢都將基於 Token,它是一個sessionId
token
- GUID,所以我在該列上創建了聚集索引。我們的 DBA 是 SQL Server MVP,他告訴我在 GUID 列上使用聚集索引會導致碎片,我應該在
IDENTITY
列上創建聚集索引,並為該列創建非聚集索引Token
。不明白,非聚集索引不會有同樣的碎片問題嗎?為什麼將數據複製到新索引中比使用聚集索引更好?
token-Guid 不是 PK,PK 是所有列的組合(包括
InsertDate
)。目前(因為我真的不知道什麼原因)所有 GUID 值都儲存為
NVARCHAR(100)
,我不知道歷史原因是什麼,但這就是我們所擁有的。我們的 DBA 在海外,我們的交流是一條很長的推文,所以在他回來之前我無法得到正確的答案
由於您表示插入性能是主要關注點,因此我會採納 DBA 的建議並將分群鍵設為標識列,因為它是唯一的、單調遞增的數字,保證(幾乎)不會導致頁面拆分在桌子上。
此外,不要將 GUID 儲存在
nvarchar(100)
列中,請使用為它們設計的數據類型,即uniqueidentifier
.在向此表添加索引之前,請仔細查看報告要求。您可能會發現根本不需要 GUID 列上的索引。如果您確定需要在 GUID 列上建立索引,您可能會發現希望索引的前導列不是 GUID,也許它會是
Group
orTestID
列?如果您已通過嚴格的設計流程確定針對此表的大多數報告查詢將使用會話 ID 作為連接或 where 子句的主要組成部分,那麼您實際上可能會受益於在會話上聚集的表ID。您可能希望將頁面填充因子設置為低於 100%,可能從 93% 開始並監控碎片級別。此外,值得指出的是,如果將此表儲存在 SSD 上,碎片可能不會成為一個大問題。會話 ID 會在其他相關表中傳播嗎?如果是這樣,您可能還需要在其他表中的列上使用非聚集索引,從而加劇碎片問題。
IF OBJECT_ID('dbo.SessionSurrogate') IS NOT NULL DROP TABLE dbo.SessionSurrogate; CREATE TABLE dbo.SessionSurrogate ( SessionID int NOT NULL CONSTRAINT PK_SessionSurrogate PRIMARY KEY NONCLUSTERED IDENTITY(1,1) , SessionGUID uniqueidentifier NOT NULL ); CREATE UNIQUE INDEX CI_SessionSurrogate --could be non-unique if required ON dbo.SessionSurrogate(SessionGUID); IF OBJECT_ID('dbo.UserTestGroups') IS NOT NULL DROP TABLE dbo.UserTestGroups; CREATE TABLE dbo.UserTestGroups ( UserTestGroupsID int NOT NULL CONSTRAINT PK_UserTestGroups PRIMARY KEY CLUSTERED IDENTITY(1,1) , SessionID int NOT NULL , TestID bigint NOT NULL , [Group] tinyint NOT NULL , InsertDate datetime NOT NULL ); GO CREATE PROCEDURE dbo.InsertSession ( @SessionGUID uniqueidentifier , @TestID bigint , @Group tinyint ) AS BEGIN INSERT INTO dbo.SessionSurrogate (SessionGUID) OUTPUT INSERTED.SessionID, @TestID, @Group, GETDATE() INTO dbo.UserTestGroups (SessionID, TestID, [Group], InsertDate) OUTPUT INSERTED.SessionID VALUES (@SessionGUID); END; GO
然後,您將插入如下數據:
DECLARE @SessGUID uniqueidentifier = NEWID(); DECLARE @TestID bigint = 42; DECLARE @Group tinyint = 6; EXEC dbo.InsertSession @SessGUID, @TestID, @Group;
這樣做的好處是您只會在行寬非常窄(20 字節)的單個表上造成碎片,每頁提供大約 400 行。上面的儲存過程非常緊湊
int
,如果需要,可以輸出生成的供您以後使用。