SQL Server:為現有索引創建新的 GUID 值
我有一個包含大約 600 萬行的現有表。相關欄目是:
ID int not null PK Key uniqueidentifier not null
通過查找在此表上讀/寫
Key
可能是 100 比 1。現有行都是使用創建的,
newid()
因此它們不是按順序排列的。存在 的索引Key
。CREATE NONCLUSTERED INDEX [idx_Robert] ON [dbo].[Aleksander] ( [Key] ASC ) WITH (PAD_INDEX = OFF , STATISTICS_NORECOMPUTE = OFF , SORT_IN_TEMPDB = OFF , DROP_EXISTING = OFF , ONLINE = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON , FILLFACTOR = 80 ) ON [PRIMARY]
鑑於表中已經有數百萬行,將 GUID 生成更改為順序使用有什麼好處
newsequentialid()
嗎?展望未來,它將生成更好的數據,但由於不知道新的順序 GUID 系列將在索引中的哪個位置著陸,它會更快地搞砸索引嗎?讓 GUID 生成不連續會更好嗎,以便索引中剩餘的頁面空間更均勻地填充?
就表格的增長率而言,它代表了大約3年的數據。
Year NumRows 2012 3962660 2013 1661189 2014 711241
截至這個問題,2014 年是一年的一半。在某些時候有一些程式碼更改降低了插入率,所以我相信 2013 年將是典型的年度行數。
您可以通過遷移到
NEWSEQUENTIALID()
.您目前每天在表中插入大約 4,500 行。該表目前大約有 630 萬行。這大約是每天餐桌的 0.7%。假設對錶的每次插入都會導致索引頁拆分,這將導致每天將 9,000 頁寫入儲存系統。這將為插入索引和重建/重組產生一些不必要的 I/O 壓力。將欄位切換
Key
為順序 ID 生成將大大減少這種 I/O 模式。如果您切換到
NEWSEQUENTIALID()
,在表上執行的程式碼INSERTs
將需要對嘗試插入重複項的可能性保持敏感Key
。這是一種遙遠的可能性,但它仍然是一種可能性,並且對於兩者都是相同的NEWID()
-NEWSEQUENTIALID()
因為沒有什麼是由電腦生成的真正隨機的。通過使索引唯一,針對索引的查詢可以獲得一些好處,如下所示:CREATE UNIQUE NONCLUSTERED INDEX [idx_Robert] ON [dbo].[Aleksander] ( [Key] ASC ) WITH ( PAD_INDEX = OFF , STATISTICS_NORECOMPUTE = OFF , SORT_IN_TEMPDB = OFF , DROP_EXISTING = OFF , ONLINE = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON , FILLFACTOR = 80 ) ON [PRIMARY];
此更改將使索引掃描的可能性降低。Paul White 在https://sqlkiwi.blogspot.com/2011/02/seeking-without-indexes.html上有一篇關於此的優秀文章
由於您沒有給出任何關於您對該表執行的查詢類型的指示,因此我假設
您在某些時候正在掃描整個索引,而與您的情況無關。如果您的數據真正儲存在單個旋轉磁碟上,那麼讓您的索引不分段應該可以減少查詢返回資訊所需的時間。話雖如此,通過改進 IO 子系統,您可能會獲得更多的錢(時間就是金錢),因此索引重建/重組是不必要的。請參閱http://www.brentozar.com/archive/2012/08/sql-server-index-fragmentation/。
如果您有興趣測量您對
Key
列使用的索引設置的效果,您可以使用系統 dmv 監控索引使用的頁面的頁面空閒百分比(除其他外)sys.dm_exec_index_physical_stats
。例如:SELECT o.name, ps.avg_page_space_used_in_percent, ps.avg_fragmentation_in_percent FROM sys.dm_db_index_physical_stats(DB_ID(),OBJECT_ID('KeyTable','Table'), NULL, NULL, 'DETAILED') ps INNER JOIN sys.objects o ON ps.object_id = o.object_id;
(您可能需要調整傳遞的參數以僅顯示
Key
列上的索引。)一旦
avg_page_space_used_in_percent
跨越某個邊界值,例如 95%(或更多),您將重新建構索引,這將對其進行碎片整理,並通過適當的設置,在每個頁面上為具有隨機生成值FILL_FACTOR
的新行留出一些空間。NEWID()
如果您確實決定使用
NEWSEQUENTIALID()
索引,則可以使用FILL_FACTOR
100%,因為索引頁面將不再頻繁拆分。這樣做的好處是將保存索引所需的頁數減少了 20%(相對於您目前的設置),從而減少了在必要時重建索引所需的 IO。由於您每天僅以大約佔整個表的 0.07% 的速度插入新行,因此系統可能需要很長時間才能自動觸發對所涉及表的統計資訊的直方圖更新。您可以使用該
STATS_DATE()
功能來確保統計數據不會超過x
幾天。最新的統計資訊對於查詢引擎生成最佳查詢計劃至關重要。
“……使用 newsequentialid() 將 GUID 生成更改為順序生成有什麼好處嗎?”
不。
僅當 GUID 列上有聚集索引並且您希望避免插入導致的頁面拆分時,順序 GUID 才適用。
編輯以解決以下評論:插入數據時,所有非聚集索引都會遭受頁面拆分。例如,當您輸入 Homer Simpson 的記錄時,它會輸入到 LastName 索引的“S”葉頁中,這可能會導致頁面拆分。但是,您不要求客戶按嚴格的字母順序加入。
此外,用於索引葉頁和非葉頁的閂鎖系統意味著頁拆分比數據頁上的頁拆分需要更少的處理時間和資源。
除此之外,OP 需要什麼才能更改為順序 ID?他們必須將 Key 列上的 Default 約束替換為 NEWSEQUENTIALID()。這不會影響表中的任何現有行(這很好,因為有外鍵使用這些鍵) - 只是新行。從那時起,插入的行將具有增加的鍵,但這些增加的鍵不一定會大於表中的現有數據(NEWSEQUENTIALID() 僅保證 GUID 大於由 NEWSEQUENTIALID() 生成的任何其他 GUID該電腦自重新啟動以來)。這意味著誰的插入仍然會導致非聚集索引中的頁面拆分!