归档当前年份以外的所有数据并同时对表进行分区的最佳方法是什么


23

任务

从一组大型表中存档,除了滚动13个月以外的所有时间。存档的数据必须存储在另一个数据库中。

  • 数据库处于简单恢复模式
  • 这些表是5000万行到数十亿行,在某些情况下,每行占用数百GB。
  • 这些表当前未分区
  • 每个表在不断增加的日期列上都有一个聚集索引
  • 每个表还具有一个非聚集索引
  • 对表的所有数据更改都是插入
  • 目标是最大程度地减少主数据库的停机时间。
  • 服务器是2008 R2 Enterprise

“存档”表将包含约11亿行,“活动”表将包含约4亿行。显然,存档表会随着时间的推移而增加,但是我希望实时表也会迅速增加。至少在接下来的几年中说50%。

我曾考虑过Azure拉伸数据库,但不幸的是,我们现在使用的是2008 R2,并且可能会在其中停留一段时间。

当前计划

  • 创建一个新的数据库
  • 在新数据库中创建按月分区的新表(使用修改的日期)。
  • 将最近的12-13个月的数据移到分区表中。
  • 对两个数据库进行重命名交换
  • 从现在的“归档”数据库中删除移动的数据。
  • 对“归档”数据库中的每个表进行分区。
  • 将来使用分区交换来存档数据。
    • 我的确意识到,我将不得不交换要存档的数据,将该表复制到存档数据库,然后将其交换到存档表中。这是可以接受的。

问题: 我正在尝试将数据移到初始分区表中(实际上,我仍在对其进行概念验证)。我正在尝试使用TF 610(根据《数据加载性能指南》)和一条INSERT...SELECT语句来移动数据,最初认为该数据将被最少地记录。不幸的是,每次我尝试将其完全记录下来。

在这一点上,我认为我最好的选择可能是使用SSIS包移动数据。我试图避免这种情况,因为我正在使用200个表,而我可以通过脚本轻松地生成和运行的任何事情。

我的总体计划中是否缺少任何内容?SSIS是否是我最好的选择,它可以快速移动数据并以最少的日志使用量(空间问题)?

没有数据的演示代码

-- Existing structure
USE [Audit]
GO

CREATE TABLE [dbo].[AuditTable](
    [Col1] [bigint] NULL,
    [Col2] [int] NULL,
    [Col3] [int] NULL,
    [Col4] [int] NULL,
    [Col5] [int] NULL,
    [Col6] [money] NULL,
    [Modified] [datetime] NULL,
    [ModifiedBy] [varchar](50) NULL,
    [ModifiedType] [char](1) NULL
); 
-- ~1.4 bill rows, ~20% in the last year

CREATE CLUSTERED INDEX [AuditTable_Modified] ON [dbo].[AuditTable]
(   [Modified] ASC   )
GO


-- New DB & Code
USE Audit_New
GO

CREATE PARTITION FUNCTION ThirteenMonthPartFunction (datetime)
AS RANGE RIGHT FOR VALUES ('20150701', '20150801', '20150901', '20151001', '20151101', '20151201', 
                            '20160101', '20160201', '20160301', '20160401', '20160501', '20160601', 
                            '20160701') 

CREATE PARTITION SCHEME ThirteenMonthPartScheme AS PARTITION ThirteenMonthPartFunction
ALL TO ( [PRIMARY] );

CREATE TABLE [dbo].[AuditTable](
    [Col1] [bigint] NULL,
    [Col2] [int] NULL,
    [Col3] [int] NULL,
    [Col4] [int] NULL,
    [Col5] [int] NULL,
    [Col6] [money] NULL,
    [Modified] [datetime] NULL,
    [ModifiedBy] [varchar](50) NULL,
    [ModifiedType] [char](1) NULL
) ON ThirteenMonthPartScheme (Modified)
GO

CREATE CLUSTERED INDEX [AuditTable_Modified] ON [dbo].[AuditTable]
(
    [Modified] 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) ON ThirteenMonthPartScheme (Modified)
GO

CREATE NONCLUSTERED INDEX [AuditTable_Col1_Col2_Col3_Col4_Modified] ON [dbo].[AuditTable]
(
    [Col1] ASC,
    [Col2] ASC,
    [Col3] ASC,
    [Col4] ASC,
    [Modified] 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) ON ThirteenMonthPartScheme (Modified)
GO

移动密码

USE Audit_New
GO
DBCC TRACEON(610);

INSERT INTO AuditTable
SELECT * FROM Audit.dbo.AuditTable
WHERE Modified >= '6/1/2015'
ORDER BY Modified

RE“移动数据”:为了最大程度地减少日志使用量,您可以批量移动数据,例如dba.stackexchange.com/a/139009/94130中的Approch 2” 。关于分区,您是否考虑过分区视图?
Alex

@Alex Yea,我已经考虑了两者。我的备份计划是使用SSIS批量移动数据。对于这种特殊情况,我的问题恰好是为分区建立的。(通过切换快速加载/卸载数据)
Kenneth Fisher

Answers:


10

为什么没有得到最少的日志记录?

我发现您参考的《数据加载性能指南》是非常宝贵的资源。但是,它也不是100%全面的,我怀疑网格已经足够复杂,以至于作者没有添加一列Table Partitioning来打破行为的差异,具体取决于接收插入的表是否已分区。正如我们将在后面看到的那样,表已被分区的事实似乎抑制了最少的日志记录。

在此处输入图片说明

推荐方法

根据《数据加载性能指南》中的建议(包括“批量加载分区表”部分)以及丰富的加载数百亿行的分区表的经验,以下是我建议的方法:

  • 创建一个新的数据库。
  • 在新数据库中创建按月分区的新表。
  • 以以下方式移动最近一年的数据:
    • 为每个月创建一个新的堆表;
    • 使用TABLOCK提示将当月的数据插入堆中;
    • 将聚集索引添加到包含该月数据的堆中;
    • 添加检查约束,以强制该表仅包含本月的数据;
    • 将表切换到新的整体分区表的相应分区。
  • 对两个数据库进行重命名交换。
  • 截断现在“归档”数据库中的数据。
  • 对“归档”数据库中的每个表进行分区。
  • 将来使用分区交换来存档数据。

与原始方法相比的差异:

  • 如果您TABLOCK一次使用分区切换将数据放入分区表中,一次一次加载到堆中,则移动最近12-13个月的数据的方法将更加有效。
  • DELETE清除旧表的A 将被完全记录。也许您可以TRUNCATE或者删除该表并创建一个新的存档表。

比较移动最近一年数据的方法

为了在合理的时间内比较计算机上的方法,我使用了100MM row生成的并遵循您的模式的测试数据集。

从下面的结果中可以看到,通过使用TABLOCK提示将数据加载到堆中,可以大大提高性能并减少日志写入。如果一次完成一个分区,则还有一个额外的好处。还值得一提的是,如果一次运行多个分区,则一次轻松分区的方法可以轻松地进一步并行化。根据您的硬件,这可能会带来很大的提升。我们通常会在服务器级硬件上一次至少加载四个分区。

在此处输入图片说明

这是完整的测试脚本

最后的笔记

所有这些结果在某种程度上取决于您的硬件。但是,我的测试是在具有旋转磁盘驱动器的标准四核笔记本电脑上进行的。如果您使用的是体面的服务器,而该服务器在执行此过程时没有很多其他负载,则数据加载可能会更快。

例如,我在实际的开发服务器(Dell R720)上运行了推荐的方法,并发现减少了76 seconds(从156 seconds笔记本电脑上)。有趣的是,插入分区表中的原始方法并未获得相同的改进,而仅接管12 minutes了开发服务器。大概是因为这种模式产生了一个串行执行计划,并且我的笔记本电脑上的单个处理器可以与开发服务器上的单个处理器匹配。


再次感谢Geoff。我正在使用SWITCH方法。具体来说,我正在使用SSIS和动态SQL来并行运行13个月。
肯尼斯·费舍尔

1

这可能是Biml的良好候选人。一种方法是创建一个可重用的模板,该模板将使用For Each容器在较小的日期范围内迁移单个表的数据。Biml将遍历您的表集合以为每个符合条件的表创建相同的包。安迪伦纳德(Andy Leonard)在他的楼梯系列中有一个介绍。


0

也许不用创建新数据库,而是将实际数据库还原到新数据库并删除最新的12-13个月的数据。然后在您的真实数据库中删除您刚创建的存档区域中未包含的数据。如果大删除是一个问题,也许您可​​以通过脚本删除10K或更大的集合。

您的分区任务似乎不会受到干扰,并且似乎适用于您删除后的任何一个数据库。


我以前用较小的数据库完成过此任务。鉴于当前的大小以及我想在两边都使用分区表这一事实,我认为此方法实际上会占用更长的时间,并且需要更多的空间(最小时为当前DB大小的两倍)
Kenneth Fisher
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.