短背景
大家好。我目前面临的情况是,我需要在 SQL Server 标准版的一个非常大的表上提高性能。该表是事务繁重的,预计会获得更多的事务。
解决方案第 1 部分
我决定使用类似于 Barry King 在这里提出的策略将表分成几个部分。所以我有一堆看起来像下面这样的表格。
CREATE TABLE [NewTable001](
[DayId] [int] NOT NULL,
[OriginalTableId] [bigint] NOT NULL,
[CustomerId] [int] NULL
CONSTRAINT [PK_OriginalTableID001] PRIMARY KEY CLUSTERED
(
[OriginalTableID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [NewTable001] WITH CHECK ADD CONSTRAINT [CK_Day_001] CHECK ([DayId]=(1))
GO
ALTER TABLE [NewTable001] CHECK CONSTRAINT [CK_Day_001]
GO
我也有相应的视图来处理所有基础表。现在的问题,正如 Barry King 在他的博客文章中所说,是视图中唯一 ID 的处理。由于视图无法处理 ID,因此它们需要存储在表中并且无法利用身份功能。
解决方案第 2 部分
通过像这样创建一个单独的表:
create table PartitionIDHelper
(
ID bigint identity(10000001,1), --Hihger (with margin) than the higest value in current table
Value int --Something that is quick to write
);
然后在我的程序中使用此代码:
declare @ID bigint;
insert into PartitionIDHelper
select 1; --Just a value so that I can get the unique ID from the table
select @ID = @@IDENTITY; --My shiny new id value to use when writing to the view</pre>
潜在问题
此解决方案的潜在问题是我可能以错误的顺序将值写入 [OriginalTableID]。
假设进程 A 先于进程 B 启动。进程 A 还在进程 B 之前从 PartitionIDHelper 表中获取 ID。然而,进程 B 更快,并且在进程 B 之前写入,导致在进程 A 之前写入我的聚集索引。
然后,进程 A 不会写入索引中的“最佳”位置,因为它是无序的。
潜在的解决方案
那么如何最好地解决这个问题呢?我可以想到两种策略。
- 接受我的聚集索引会稍微出问题
- 将每个表中的本地标识列用于聚集索引。
潜在的解决方案 2 在我看来是一条糟糕的道路,因为除了存储顺序之外,我永远不会将该列用于其他任何事情,因为它永远不会被排序、在 where 子句中使用甚至被选中。
在阅读了 Michelle Ufford 的这篇博文(大部分内容)之后,我倾向于坚持使用略微分散的聚集索引。下面的引用对我提出了一个难题,因为数据主要由单例查询访问,但它是一个插入速度很重要的 OLTP 系统。
我并不是建议您只在标识整数列上创建聚集索引。碎片化虽然通常是不可取的,但主要影响范围扫描查询;单例查询不会有太大影响。即使是范围扫描查询也可以从常规碎片整理工作中受益。然而,集群键的不断增加的属性是需要考虑的,并且在插入速度很重要的 OLTP 系统中尤其重要。
任何和所有的意见和建议将不胜感激!
/塔赫