SQL Server 當表分割槽遇上唯一約束(轉載)
一、前言
我已經在高興對伺服器建立了表分割槽並且獲得良好效能和自動化管理分割槽切換的時候,某一天,開發人員告訴我,某表的兩個欄位的資料不唯一,需要為這兩個欄位建立唯一索引的時候,這一切就變得不完美了。
列的唯一,這個實際上是一個唯一索引。使用關鍵字unique建立。
二、背景
我有一個表TestUnique,這個表使用分割槽方案[Sch_TestUnique_Id],它是以Id做為分割槽依據列的,這個Id也是一個聚集索引,表中其它索引是跟分割槽對齊的(建立其它非聚集索引的時候使用了分割槽方案或者不指定-預設就是分割槽方案),而且我我這個表很大,我需要定時的進行交換分割槽(SWITCH PARTITION、滑動視窗、切換分割槽),表分割槽的相關資訊可參考:
--建立測試表 CREATE TABLE [dbo].[TestUnique]( [Id] [int] IDENTITY(600000000,1) NOT FOR REPLICATION NOT NULL, [SiteId] [int] NULL, [Url] [nvarchar](420) NULL, [PublishOn] [datetime] NULL, [AddOn] [datetime] NULL, CONSTRAINT [PK_Archive] PRIMARY KEY CLUSTERED( [Id] ASC )WITH (PAD_INDEX = ON, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 100) ON [Sch_TestUnique_Id]([Id]) ) ON [Sch_TestUnique_Id]([Id]) GO
現在需要建立SiteId+Url做為一個唯一索引,本來以為這個唯一索引是可以進行分割槽對齊的,但是卻在建立索引的時候遇到錯誤了。
三、分析
1. 對分割槽表建立索引時,SQL Server 將使用與該表相同的分割槽方案和分割槽依據列自動對索引進行分割槽。因此,索引的分割槽方式實質上與表的分割槽方式相同。這將使索引與表“對齊”。建立唯一索引有下面三種方式:
--方式1 CREATE UNIQUE NONCLUSTERED INDEX [IX_TestUnique_SiteIdUrl] ON [TestUnique] (SiteId,Url)
訊息1908,級別16,狀態1,第1 行 列'Id' 是索引'IX_TestUnique_SiteIdUrl' 的分割槽依據列。唯一索引的分割槽依據列必須是索引鍵的子集。
--方式2 ALTER TABLE [dbo].[TestUnique] ADD CONSTRAINT [IX_TestUnique_SiteIdUrl] UNIQUE NONCLUSTERED ( [SiteId] ASC, [Url] ASC )WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) ON [Sch_TestUnique_Id]([Id])
訊息1908,級別16,狀態1,第1 行 列'Id' 是索引'IX_TestUnique_SiteIdUrl' 的分割槽依據列。唯一索引的分割槽依據列必須是索引鍵的子集。 訊息1750,級別16,狀態0,第1 行 無法建立約束。請參閱前面的錯誤訊息。
--方式3 CREATE UNIQUE NONCLUSTERED INDEX [IX_TestUnique_SiteIdUrl] ON [dbo].[TestUnique] ( [SiteId] ASC, [Url] ASC )WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = ON, ONLINE = OFF) ON [Sch_TestUnique_Id]([Id]) GO
--測試沒有指定分割槽方案時是否預設使用分割槽方案 CREATE NONCLUSTERED INDEX [IX_TestUnique_SiteIdUrl] ON [dbo].[TestUnique] ( [SiteId] ASC, [Url] ASC )WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF) GO
2. 如果分割槽依據列不可能包含在唯一鍵中,則必須使用 DML 觸發器,而不是強制實現唯一性。(在需要分割槽的表中,估計插入的資料量還是比較大的,在這個表使用觸發器應該會有效能上的問題吧?)
--測試索引鍵的子集 CREATE UNIQUE NONCLUSTERED INDEX [IX_TestUnique_SiteIdUrl] ON [dbo].[TestUnique] ( [Id] ASC, [SiteId] ASC, [Url] ASC )WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = ON, ONLINE = OFF) ON [Sch_TestUnique_Id]([Id])
上面這條SQL是能成功執行的,不過從業務邏輯上來看,加了唯一的Id值對唯一就沒有任何意義了,但是這條SQL告訴我們:當你使用了SiteId做為分割槽依據列,那麼你就可以建立以SiteId+Url的唯一索引。
3. IGNORE_DUP_KEY = ON與IGNORE_DUP_KEY = OFF的區別:
忽略重複鍵,在建立或修改唯一索引時,可以可設定一個忽略重複鍵的選項。如果此選項已設定為“是”(ON),當您試圖通過新增影響多行的資料來建立重複鍵(使用 INSERT 語句)時,則不會新增包含重複項的行,不重複的記錄會給插入到表中的;如果此選項設定為“否”(OFF),則整個插入操作將失敗,並且將回滾所有資料。
4. 如果您預計將通過使用更多分割槽來擴充套件索引,或者將會涉及到頻繁的分割槽切換,那麼將索引與已分割槽表對齊將非常重要。有關詳細資訊,請參閱設計分割槽以管理資料子集。如果表與其索引對齊,SQL Server 則可以快速高效地切換分割槽,同時又能維護表及其索引的分割槽結構。
5. 在下列情況下,獨立於基表而單獨設計已分割槽索引(不對齊)很有用:
- 基表未分割槽。
- 索引鍵是唯一的,不包含表的分割槽依據列。
- 您希望基表與使用不同聯接列的多個表一起參與組合聯接。
四、注意
1. 索引要與其基表對齊,並不需要與基表參與相同的命名分割槽函式。但是,索引和基表的分割槽函式在實質上必須相同,即:
- 分割槽函式的引數具有相同的資料型別;
- 分割槽函式定義了相同數目的分割槽;
- 分割槽函式為分割槽定義了相同的邊界值。
2. 若要啟用分割槽切換,表的所有索引都必須對齊。
3. 如果在建立時指定了不同的分割槽方案或單獨的檔案組來儲存索引,則 SQL Server 不會將索引與表對齊。
五、總結
1. 如果不需要進行交換分割槽的情況下,並且你那麼幸運讓唯一索引列包含了分割槽依據列的話,你完全可以讓唯一與表分割槽對齊,而且不用擔心交換分割槽的影響;
2. 如果不需要進行交換分割槽的情況下,唯一索引不包含分割槽依據列,那就讓唯一索引單獨使用一個檔案組,這樣效能也能得到一部分的提升;
3. 如果需要進行交換分割槽的情況下,唯一索引不包含分割槽依據列,那就讓唯一索引單獨使用一個檔案組,但是你需要在進行交換分割槽之前:停止TCP/IP防止進資料,重啟服務,刪除唯一索引,交換分割槽,建立唯一索引,啟用TCP/IP,重啟服務;(貌似這不是個好方法,歡迎大家提供好的方案)
六、參考文獻
定義了索引檢視時的分割槽切換
使用分割槽切換高效傳輸資料
已分割槽索引的特殊指導原則(唯一索引)
設計分割槽以管理資料子集