udh*_*gam 2 sql-server filegroups index-maintenance sql-server-2017
我手头有一项任务是移动重建一个大表,以将 LOB 页面移动到 SQL Server 2017 Enterprise Edition 上的不同文件组。
我正在概念验证环境中测试脚本,我可以看到总共大约CREATE INDEX .. DROP_EXISTING=ON
需要 6 小时。
CREATE UNIQUE CLUSTERED INDEX [PK_TABLE1]
ON [dbo].[TABLE1] ([Id] ASC)
WITH (DROP_EXISTING = ON , FILLFACTOR = 100, PAD_INDEX = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, IGNORE_DUP_KEY = OFF, DATA_COMPRESSION = NONE, STATISTICS_NORECOMPUTE = OFF, ONLINE = ON, MAXDOP=2)
ON PS_MOVE_HELPER_D59E24BC73414AA8A5FB2E5D8F93C3D8([Id] );
CREATE UNIQUE CLUSTERED INDEX [PK_TABLE1]
ON [dbo].[TABLE1] ([Id] ASC)
WITH (DROP_EXISTING = ON , FILLFACTOR = 100, PAD_INDEX = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, IGNORE_DUP_KEY = OFF, DATA_COMPRESSION = NONE, STATISTICS_NORECOMPUTE = OFF, ONLINE = ON, MAXDOP=2)
ON [LOB_DATA];
CREATE UNIQUE CLUSTERED INDEX [PK_TABLE1]
ON [dbo].[TABLE1] ([Id] ASC)
WITH (DROP_EXISTING = ON , FILLFACTOR = 100, PAD_INDEX = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, IGNORE_DUP_KEY = OFF, DATA_COMPRESSION = NONE, STATISTICS_NORECOMPUTE = OFF, ONLINE = ON, MAXDOP=2)
ON [ROW_DATA];
Run Code Online (Sandbox Code Playgroud)
auto_stats
事件。您可以跟踪扩展事件的进度progress_report_online_index_operation
,但这不会显示单独的统计构建事件,因为根本不存在该事件。您的进程缓慢是因为您正在执行大量资源密集型工作,而不是因为统计数据刷新。在微软提供一种方法来支持您直接需要的操作(将单个分配单元移动到不同的文件组)之前,这实际上是无法避免的。
辅助分区方案的第一次重建将是单线程的,并且具有不必要的排序。
在 LOB 文件组上重建索引的成本很高,即使您只是为了删除分区而这样做。SQL Server 不知道您在做什么,因此只会按照您的要求重建整个表。
第三次重建也很昂贵,但这个过程确实可以让您最终到达需要去的地方,同时保持表大部分在线。
BULK_LOGGED
使用设置为或恢复模型的数据库执行重建,SIMPLE
以尽可能利用最少的日志记录。
SWITCH
如果能够与源表一起使用,则可以避免一次重建和单线程排序:
PRIMARY
。SWITCH
将源表写入分区表。这应该是仅瞬时元数据操作。无论如何指定该WAIT_AT_LOW_PRIORITY
选项。LOB_DATA
文件组。
LOB_DATA
文件组上的所有分配单元。ROW_DATA
文件组。您现在拥有包含文件组上的 LOB 数据的原始表LOB_DATA
以及文件组上的其他所有内容ROW_DATA
。
重新组织 Stack Overflow 示例数据库中的 Users 表的演示(首先创建文件组):
-- Temporary partitioning function and scheme
CREATE PARTITION FUNCTION PF (integer) AS RANGE FOR VALUES ();
CREATE PARTITION SCHEME PS AS PARTITION PF ALL TO ([PRIMARY]);
-- Switch table
CREATE TABLE [dbo].[Users_Switch]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[AboutMe] [nvarchar](max) NULL,
[Age] [int] NULL,
[CreationDate] [datetime] NOT NULL,
[DisplayName] [nvarchar](40) NOT NULL,
[DownVotes] [int] NOT NULL,
[EmailHash] [nvarchar](40) NULL,
[LastAccessDate] [datetime] NOT NULL,
[Location] [nvarchar](100) NULL,
[Reputation] [int] NOT NULL,
[UpVotes] [int] NOT NULL,
[Views] [int] NOT NULL,
[WebsiteUrl] [nvarchar](200) NULL,
[AccountId] [int] NULL,
CONSTRAINT [PK_Users_Switch_Id]
PRIMARY KEY CLUSTERED ([Id] ASC)
ON PS (Id)
) ON PS (Id);
-- Optional, to match source table
EXECUTE sys.sp_tableoption
@TableNamePattern = N'dbo.Users_Switch',
@OptionName = 'large value types out of row',
@OptionValue = 'on';
BEGIN TRY;
BEGIN TRANSACTION;
-- Switch
ALTER TABLE dbo.Users
SWITCH TO dbo.Users_Switch
PARTITION 1
WITH
(
WAIT_AT_LOW_PRIORITY
(
MAX_DURATION = 1 MINUTES,
ABORT_AFTER_WAIT = SELF
)
);
-- Drop original
DROP TABLE dbo.Users;
-- Rename table
EXECUTE sys.sp_rename
@objname = N'dbo.Users_Switch',
@newname = N'Users',
@objtype = 'OBJECT';
-- Rename primary key
EXECUTE sys.sp_rename
@objname = N'PK_Users_Switch_Id',
@newname = N'PK_Users_Id',
@objtype = 'OBJECT';
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
THROW;
END CATCH;
-- Move everything to LOB_DATA
-- Parallel, no sort
CREATE UNIQUE CLUSTERED INDEX [PK_Users_Id]
ON dbo.Users (Id)
WITH (ONLINE = ON, DROP_EXISTING = ON)
ON LOB_DATA;
-- Drop temporary partitioning function and scheme
DROP PARTITION SCHEME PS;
DROP PARTITION FUNCTION PF;
-- Move non-LOB data to ROW_DATA
-- Also parallel, no sort
CREATE UNIQUE CLUSTERED INDEX [PK_Users_Id]
ON dbo.Users (Id)
WITH (ONLINE = ON, DROP_EXISTING = ON)
ON ROW_DATA;
-- Done
Run Code Online (Sandbox Code Playgroud)
要将 Users 表重置回所有内容PRIMARY
:
IF EXISTS
(
SELECT *
FROM sys.partitions AS P
JOIN sys.allocation_units AS AU
ON P.hobt_id = AU.container_id
JOIN sys.filegroups AS FG
ON FG.data_space_id = AU.data_space_id
WHERE
P.[object_id] = OBJECT_ID(N'dbo.Users', 'U')
AND FG.[name] != N'PRIMARY'
)
BEGIN
-- Temporary partitioning function and scheme
CREATE PARTITION FUNCTION PF (integer) AS RANGE FOR VALUES ();
CREATE PARTITION SCHEME PS AS PARTITION PF ALL TO ([PRIMARY]);
-- Move everything to PRIMARY
-- Single-threaded, sort
CREATE UNIQUE CLUSTERED INDEX [PK_Users_Id]
ON dbo.Users (Id)
WITH (ONLINE = ON, DROP_EXISTING = ON)
ON PS (Id);
-- Make table non-partitioned
CREATE UNIQUE CLUSTERED INDEX [PK_Users_Id]
ON dbo.Users (Id)
WITH (ONLINE = ON, DROP_EXISTING = ON)
ON [PRIMARY];
-- Drop temporary partitioning function and scheme
DROP PARTITION SCHEME PS;
DROP PARTITION FUNCTION PF;
END;
Run Code Online (Sandbox Code Playgroud)
* 第一次从非分区表重建分区表是此规则的一个例外示例。此索引构建可能会导致单独的统计信息刷新,但前提是 SQL Server 认为现有统计信息已过时。到目前为止我还无法重现这一点,所以它仍然只是一种可能性。