【问题标题】:Efficient way to alter 100GB table更改 100GB 表的有效方法
【发布时间】:2011-04-11 18:56:23
【问题描述】:

我们有许多数据库,在其中一个表中存储了 10 到 100 GB 的数据。它包含图像数据。问题是这些数据库中有很多是不正确地创建的。基本上主键实际上并不是主键。是使用可空列上的唯一索引创建的。其中一些具有 int 作为主键,而不是 bigint。

所以我们一直在慢慢地检查和修复这些数据库。它们在 SQL Server 2000 到 SQL Server 2008 上运行,尽管大多数有主键问题的都在 SQL Server 2000 上。问题是,我们不想在转换表时将数据库锁定一整天。我们采用了几种策略:

  1. 告诉 SQL Server 直接更改列类型。这会锁定表直到它完成,并且在许多情况下将它放置一夜之后,它仍然没有完成。

  2. 一次性将所有图像插入到新表中。这更容易被中断,但整个表基本上都被写入了进程中的日志文件。

  3. 在目标表中不存在行时插入 100 行。好处是他们可以在此过程中继续使用数据库(性能受到很大影响),并且可以在任何时候任意停止和重新启动它,并且它可以防止 100GB 以上的日志文件。这就是我们目前正在做的事情,但是随着目标表变得越来越大,查找不存在的前 100 行会变得非常慢。 UPDATE STATISTICS 和 DBCC INDEXDEFRAG 有很大帮助,但在最近的尝试中,我们达到了一个点,甚至一次有 100 张图像坐在那里没有响应。

    INSERT INTO %s  
      SELECT TOP 100 Source.*  
      FROM %s AS Source WITH (NOLOCK)  
      LEFT OUTER JOIN %s AS Target WITH (NOLOCK) ON Source.DocumentID = Target.DocumentID  
      WHERE Target.DocumentID IS NULL  
      ORDER BY Source.DocumentID  
    

所以问题是,有没有一种可以高效且可恢复的方式复制批量数据的选项?它不一定要 100% 准确,只要它完成了 99% 的工作,我们总是可以在最后返回并修复任何差异。

【问题讨论】:

    标签: sql-server-2000 bulkinsert


    【解决方案1】:

    连接是问题所在。不要那样做。只需使用 current 聚集索引,以合理的时间间隔遍历当前表。比如:

    Declare @idrange int;
    Set @idrange = 1;
    
    WHILE @idrange < 10000000
    
    INSERT INTO Destination
      SELECT *  
      FROM Source
      WHERE DocumentID between @idrange and @idrange + 999
      ORDER BY Source.DocumentID  
    
    Set @idrange = @idrange + 1000
    End 
    

    请注意,为了获得最佳速度,请从目标表中删除所有索引(包括聚集索引),然后在插入所有行后添加索引。

    编辑:更改范围间隔以防止重叠(因为 BETWEEN 包括端点)

    最后澄清一点:我的示例脚本的总体要点是,您只是想以某种合理的顺序遍历当前记录,然后将它们全部分批放入新表中。没有理由每次都继续检查目标表,因为您应该已经知道放在那里的内容以及还剩下的内容。大多数时候,使用聚集索引(如果有的话)是有意义的,因为这意味着它可以遍历表的物理顺序而无需进行书签查找。如果表没有集群,那么就使用最有意义的东西(可能是你的 PK)。

    【讨论】:

    • 这个表的主键是非聚集的。转换为聚集索引几乎和重新创建表一样痛苦,因为它必须物理地重新排列所有数据。
    • 按范围做肯定比join方法效率高。我将不得不看看它在实践中的表现如何,但我希望有某种方法可以以非原子方式执行插入,如果它在中途停止,它将留下已经完成的内容,并且避免额外写入日志文件。
    • @Bryce Wagner - 因此,如果它没有聚集在 PK 上,那么对它 聚集的任何东西进行等效范围或分组。 (请告诉我它确实有一个集群,而且它不仅仅是一个 HEAP)
    • 我的 WHILE 循环 做原子插入。如果您想真正明确地说明它,只需在插入周围加上BEGIN TRANCOMMIT TRAN。如果它确实被中断了,您必须查询表以确定它停止的位置,以便您可以在正确的位置重新启动它。
    • 它不是集群的,因为在这种情况之外,我们一次只能从这个表中拉出一行。我想你误读了我的第二条评论,回去再读一遍:我希望有一个不涉及所有事务机制的解决方案。我想要速度超过准确性(然后在最后进行清理)。但是你的回答比我之前做的要好得多。
    猜你喜欢
    • 1970-01-01
    • 2013-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多