经常重新填充大表索引
Frequently repopulated large tables indexes
我有一组数据(超过 200 万行)需要每隔几分钟重新填充一次。数据也必须在那段时间访问,所以我们有这个解决方案:
- 我们有两个 tables Table_First 和 Table_Second
- 我们有一个指向 table 之一的视图 TableView(简单 SELECT list_of_columns FROM Table_XXXX)
当视图指向 Table_First 时,我们:
- 在 Table_Second
上禁用索引
- 截断 Table_Second
- 通过 SqlBulkCopy 并行填充它
- 在 Table_Second
重建索引
- 更改 TableView,使其现在指向 Table_First
您认为这是一个好的设计,还是我们可以做得更好?
我在想,每次重建索引后,统计信息都会丢失,并且每个从 TableView 中选择数据的查询都会被重新编译。我应该担心吗?
另一个是重建实际上有时比数据插入花费更多的时间。
作为替代方案,我们可以将行插入临时 table(或持久性 table),然后使用 MERGE 与 INSERT、UPDATE 和 DELETE 更新数据。
table 看起来像这样:
CREATE TABLE [dbo].[Table_First](
[GroupId] [int] NOT NULL,
[ItemId] [int] NOT NULL,
[SKU] [nvarchar](255) NOT NULL,
[PropertyId] [int] NOT NULL,
[StringValue] [nvarchar](500) NULL,
[DecimalValue] [float] NULL,
[PropertyValueId] [int] NULL) ON [PRIMARY]
CREATE CLUSTERED INDEX [IX_Index1] ON [dbo].[Table_First]
(
[GroupId] ASC,
[PropertyId] 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)
GO
CREATE NONCLUSTERED INDEX [IX_Index2] ON [dbo].[Table_First]
(
[SKU] ASC,
[PropertyId] ASC
)
INCLUDE ([PropertyValueId]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
不幸的是,table 中的数据不是唯一的。
您的解决方案可能是可行的,但它取决于许多方面,例如您的阅读量。这里的主要瓶颈是:
- Sch-M 锁由
ALTER VIEW
语句放置在视图上,在此期间没有其他会话可以访问它。如果你能忍受,那很好;
- 更改视图将导致重新编译所有使用它的查询。如果你的查询很少,还可以接受。
但是,还有其他选择。
您可以在 table 所在的数据库中启用读取提交快照隔离 (RCSI)。之后,上述所有内容将由 SQL 服务器本身在幕后执行。
您可以使用 table 分区来进行投标。通过切换新部分和旧部分,它与您的原始计划基本相同,尽管可能没有 table 差异。但是,并非每个版本的 SQL 服务器都支持 table 分区。
我自己并没有玩太多,但是从SQL Server 2014开始,有一个很好的特性,即内存tables。它们减少了锁争用和磁盘占用空间,因此可能值得研究。
我有一组数据(超过 200 万行)需要每隔几分钟重新填充一次。数据也必须在那段时间访问,所以我们有这个解决方案:
- 我们有两个 tables Table_First 和 Table_Second
- 我们有一个指向 table 之一的视图 TableView(简单 SELECT list_of_columns FROM Table_XXXX)
当视图指向 Table_First 时,我们:
- 在 Table_Second 上禁用索引
- 截断 Table_Second
- 通过 SqlBulkCopy 并行填充它
- 在 Table_Second 重建索引
- 更改 TableView,使其现在指向 Table_First
您认为这是一个好的设计,还是我们可以做得更好?
我在想,每次重建索引后,统计信息都会丢失,并且每个从 TableView 中选择数据的查询都会被重新编译。我应该担心吗?
另一个是重建实际上有时比数据插入花费更多的时间。
作为替代方案,我们可以将行插入临时 table(或持久性 table),然后使用 MERGE 与 INSERT、UPDATE 和 DELETE 更新数据。
table 看起来像这样:
CREATE TABLE [dbo].[Table_First](
[GroupId] [int] NOT NULL,
[ItemId] [int] NOT NULL,
[SKU] [nvarchar](255) NOT NULL,
[PropertyId] [int] NOT NULL,
[StringValue] [nvarchar](500) NULL,
[DecimalValue] [float] NULL,
[PropertyValueId] [int] NULL) ON [PRIMARY]
CREATE CLUSTERED INDEX [IX_Index1] ON [dbo].[Table_First]
(
[GroupId] ASC,
[PropertyId] 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)
GO
CREATE NONCLUSTERED INDEX [IX_Index2] ON [dbo].[Table_First]
(
[SKU] ASC,
[PropertyId] ASC
)
INCLUDE ([PropertyValueId]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
不幸的是,table 中的数据不是唯一的。
您的解决方案可能是可行的,但它取决于许多方面,例如您的阅读量。这里的主要瓶颈是:
- Sch-M 锁由
ALTER VIEW
语句放置在视图上,在此期间没有其他会话可以访问它。如果你能忍受,那很好; - 更改视图将导致重新编译所有使用它的查询。如果你的查询很少,还可以接受。
但是,还有其他选择。
您可以在 table 所在的数据库中启用读取提交快照隔离 (RCSI)。之后,上述所有内容将由 SQL 服务器本身在幕后执行。
您可以使用 table 分区来进行投标。通过切换新部分和旧部分,它与您的原始计划基本相同,尽管可能没有 table 差异。但是,并非每个版本的 SQL 服务器都支持 table 分区。
我自己并没有玩太多,但是从SQL Server 2014开始,有一个很好的特性,即内存tables。它们减少了锁争用和磁盘占用空间,因此可能值得研究。