当 ID (INT) 上存在聚集索引时,根据日期对表(在 MS SQL 中)进行分区是个好主意吗

Use*_*r M 14 sql-server clustered-index partitioning

我在 MS SQL Server 中有一个表。

  • 表大小:806 GB
  • 行数:12亿
  • 索引空间:1.2 GB

表使用方法:记录从Web Service调用99.9%的使用率从伐木,开发商很少看这个表在正式版(仅当报告或研究的问题)。

主键:基于“INT”数据类型的“ID”。有一个基于“ID”列的聚集索引。

我对此更改的意图:想要管理此表(因为它有 10 年的数据)并继续前进(由于新要求),开发人员/分析师有可能进一步深入研究此表(仅几个月)我不想为同样的目的创建一个新表。

我的问题

  1. [主要问题]我可以根据“DateCreated”(DATETIME,NOT NULL 列)对这个表进行分区,而不会引起问题(逻辑上/性能方面)。

  2. [很高兴知道] 需要多少时间(我知道这取决于数据库空间/服务器内存和其他细节,但大概# 会很好)对这个巨大的表进行分区(如果可以根据日期进行分区) . 问这个问题,因为这是一个生产表,行被频繁插入(现在大约 350 条记录/分钟)。

  3. 【不完全是问题,求推荐】有没有更好的方案来管理这个表(不想在Production中保留3年以上的数据,方案在下面提到)?

当前计划(我是 MS SQL 的新手,所以这就是我想出的):

  • 在每个分区中保留 3 个月的数据。
  • 系统在每个季度前自动创建分区。
  • 在活动表中仅保留 3 年的分区。
  • 将其他分区移动到 OLD/ARCHIEVE 表(需要创建这个)。真正要清除的旧数据。

Pet*_*ier 16

简短回答:,你不能这样做。根据文档...

对聚集索引进行分区时,聚集键必须包含分区列。

这意味着为了在 上分区[DateCreated],您还必须在 上集群[DateCreated]


当您考虑分区和聚类实际上是什么时,这更有意义。

  • 一个聚集索引数据的逻辑顺序

  • 分区是一种基于函数管理数据物理存储的技术

这两个东西可以一起使用,只要它们不冲突。如果你试图让它们发生冲突,你会过得很糟糕。要逐项回答您的项目:

  1. 你不能在分区[DateCreated] 的所有除非删除并重新创建聚簇索引重建对列的表。这是可能的,但这可能是一个比您提出问题时计划的更大的项目。
  2. 没有人可以为你回答这个问题。你需要自己测试一下。几年前,当我在 GB 使用的数据和索引空间比您描述的要少的表上删除并重建聚集索引时,花了几个月的时间进行基准测试,哪种方法最安全,然后大约需要 10 个小时的下班时间在 2 个 DBA 之间努力完成重建而无需停机。添加分区方案不是该项目的一部分,但我认为它不会简化事情。
  3. 如果它真的是“一个通常没人关心的日志记录表”,那么也许您可以完成所有这些工作而不必担心保持该表在线。几乎可以肯定,如果不创建至少一个额外的表(可能还有其他几个对象),您就无法做到这一点。我鼓励您在恢复的数据库副本上尝试多种技术。我怀疑您最终会重命名当前表并重新创建它,以便在您使用您选择的任何方法存档数百个旧数据时,日志可以继续写入新的空表(大概使用分区方案)


Mic*_*een 8

如果您决定一次性执行此操作,则可能会受到磁盘速度的限制。

一个类似的操作是进行备份。备份从这里读取一大堆字节并在那里写入一大堆字节。就像你的桌子重建一样。因此,如果您的数据库是,比如说,1.6TB 并且备份需要 2 小时,那么重建一个 800GB 的表可能需要大约 1 小时(一半的大小,一半的时间)。

当然,这两项任务之间存在很多差异。备份驱动器可能与数据驱动器不同,并发工作负载会影响它们,SAN 的网络路径,二级索引,yadda yadda。所以时间会有所不同,但它们不必有数量级的不同。


与大爆炸相比,您最好按时间相反的顺序一次处理一个用例。

删除要删除的最旧数据。因为有 10 年,而您只想要 3 年,问题立即变得容易 70%!这最好通过一次删除来完成,而不是在单个查询中全部删除。如果它不能被彻底删除,如何将它移动到 OLAP 服务器或转储为压缩文件格式(parquet、ORC 和它们的同类)?

使用您想要的分区方案创建一个新的空表,并为所有 3 年的历史和一些未来定义分区。首先将最旧的数据分块移动,插入新表并从当前表中删除。我猜 ID 列是一个 IDENTITY,所以它应该与日期分区相关联。如果有人想研究这些旧数据,他们可以从这个新的分区表中进行研究。

最终,您将进入一个阶段,当前未分区表中的唯一数据是当前 3 个月范围内的数据,或者将数据拆分到两个表中会给用户带来实际痛苦。这是停电的时候。将最后一个数据移动到新的分区表,删除当前表并重命名分区表。


Ale*_*sko 7

[主要问题] 我可以根据“DateCreated”(DATETIME,NOT NULL 列)对这个表进行分区,而不会引起问题(逻辑上/性能方面)。

您可以在DateCreated将其设为聚集索引后对列进行分区。即使您设法使其成为聚集索引键,也会存在性能问题 - 您的索引将比以前(当 ID INT 聚集时)消耗更多空间(存储/内存)。

因为 INT 是 4 个字节并且是唯一值,但是您的DateCreated列是 8 个字节并且很可能不是唯一的,因此将在顶部添加额外的 4 字节 uniquifier,在某种程度上使聚集索引更重。由于聚集索引键被添加到所有非聚集索引中,它们也会变得更重

[很高兴知道] 需要多少时间(我知道这取决于数据库空间/服务器内存和其他细节,但大概# 会很好)对这个巨大的表进行分区(如果可以根据日期进行分区) . 问这个问题,因为这是一个生产表,行被频繁插入(现在大约 350 条记录/分钟)。

这完全取决于您服务器的马力

【不完全是问题,求推荐】有没有更好的方案来管理这个表(不想在Production中保留3年以上的数据,方案在下面提到)?

不要使用分区。使用具有相同结构的单独历史表。创建一个每日作业,根据DateCreated列移动(插入到历史表中/在父表中删除)所有超过 3 年的行。

不是每天一次移动所有行(超过 3 年),而是以 N 行的块为单位移动,块之间的间隔为 X 秒(或毫秒)。N 是每 1 个块删除的行数。确保 N 不会导致父表上的 Exclusive table 锁升级(通过实验找出数字)


Ste*_*o64 6

不容忽视的一点是,对于 12 亿行,您比您想象的更接近 int 数据类型的上限。所以我认为你的改变也应该计划解决这个限制,可能是改为 Bigint。

正如之前指出的,70% 的数据太旧了,您不再需要保留它,但我会考虑在要保留的 30% 上插入/选择而不是批量删除 70%,然后截断原始表。然后,您可以将行复制回或重命名表。

最后一点是,您的 Id 列可能按升序添加,这样行 < id x 较早,> id x 较晚,因此您可以构建一个映射表,将 id 映射到日期,然后直接在 id 上分区?