修改庞大数据库的策略
Strategies to modify huge database
我正在测试针对即将到来的重大变更的不同策略。问题是每个实验都会在 Azure 中产生一些成本。
数据庞大,在我认识这家公司之前,由于多年的修复和交易,可能会有一些不一致。
我需要更改 table 中包含数百万条记录和数十个索引的列。这将有一个很大的停机时间。
ALTER TABLE X ALTER COLUMN A1 decimal(15, 4) --原列为int
最初的想法之一(现在我知道这是不可能的)是有一个辅助副本,在那里进行更改,并且在更改时完成,将主要与次要交换......零或几乎零停机时间。我指的是“实时”冗余副本,而不仅仅是“副本”
编辑:
抛出新点子:
- 其中一个答案中提到的变化:创建一个 table 副本(不是整个数据库,只是 table),应用一个 INSERT INTO... SELECT 并在过程结束时交换 tables。或者...尽早进行交换以最大程度地减少停机时间,以减少在post-添加来自源
的所有记录期间的延迟交易
我试过了,但需要 AGES 才能完成。此外,一些 null 和 FK 违规会使流程在处理几个小时后失败。
“恢复”可能是一个选项,但它会使每次执行的过程变慢。没有某种“恢复”,每次失败都必须从头开始
acceptable 改进可能是忽略错误(当然要创建日志)并在迁移后应用修复。但是 afaik,AzureSql(也不是 SqlServer)不提供“忽略”选项
- 将所有索引、约束和依赖项删除到需要修改的列,修改该列并再次应用所有索引、约束和依赖项。
这个也试过了。有些索引需要 AGES 才能完成。但是 目前看来是最好的选择 。
在数据类型更改之前应用 ROW COMPRESSION 可能会有变化,但我认为它不会改善实际情况:重新创建索引
- 创建一个具有目标数据类型的新列,从源列复制数据,删除旧列并重命名新列。
此策略还需要删除和重新生成索引,因此它不会提供关于 #2 的很多收益(如果有的话)。
朋友想到了一个变种,就是把列拷贝需要的索引ONLINE复制出来。同时,将源列上的所有更改触发到列复制。
对于上述任何一种策略,都可以通过提高处理能力来获得一些收益。但是,无论如何,我们考虑使用任何一种方法来增加功率,因此这对所有解决方案都是通用的
当您需要一次性更新大量行时,使用以下迁移技术可能更有效:
- 创建新目标table
- 使用 INSERT INTO SELECT 用正确/更新的值填充新的 table
- rename新旧table
- 为新 table
创建索引
经过多次测试和备份,我们最终采用了以下方法:
- 使用所需的格式更改创建新列 [columnName_NEW]。允许 NULLS
- 为 INSERTS 创建触发器以使用要替换的列中的值更新新列
- 批量复制旧列值到新列
这个操作非常耗时。我们 运行 每天维护 window 一批(4 天 2 小时)。我们的批处理首先填充最旧行的值,我们指望触发器填充新行
- #3 完成后,不再允许在新列上使用 NULLS,但设置默认值以避免 INSERT 触发器崩溃
- 在新列上创建所有需要的索引和视图。这非常耗时,但可以在线完成
- 在旧列上允许 NULLS
- 删除插入触发器 - 现在开始停机!
- 将旧列重命名为 [columnName_OLD],将新列重命名为 [columnName]。这需要几秒钟的停机时间!
--> 你可以认为它终于完成了!
- 经过一段安全时间后,您可以备份结果并删除 [columnName_OLD] 及其所有依赖项
我选择了另一个答案,因为我认为它在大多数情况下也很有用。这个有更多的步骤,但停机时间非常短,并且在除最后一步之外的任何步骤都是可逆的。
我正在测试针对即将到来的重大变更的不同策略。问题是每个实验都会在 Azure 中产生一些成本。 数据庞大,在我认识这家公司之前,由于多年的修复和交易,可能会有一些不一致。
我需要更改 table 中包含数百万条记录和数十个索引的列。这将有一个很大的停机时间。
ALTER TABLE X ALTER COLUMN A1 decimal(15, 4) --原列为int
最初的想法之一(现在我知道这是不可能的)是有一个辅助副本,在那里进行更改,并且在更改时完成,将主要与次要交换......零或几乎零停机时间。我指的是“实时”冗余副本,而不仅仅是“副本”
编辑: 抛出新点子:
- 其中一个答案中提到的变化:创建一个 table 副本(不是整个数据库,只是 table),应用一个 INSERT INTO... SELECT 并在过程结束时交换 tables。或者...尽早进行交换以最大程度地减少停机时间,以减少在post-添加来自源 的所有记录期间的延迟交易
我试过了,但需要 AGES 才能完成。此外,一些 null 和 FK 违规会使流程在处理几个小时后失败。 “恢复”可能是一个选项,但它会使每次执行的过程变慢。没有某种“恢复”,每次失败都必须从头开始
acceptable 改进可能是忽略错误(当然要创建日志)并在迁移后应用修复。但是 afaik,AzureSql(也不是 SqlServer)不提供“忽略”选项
- 将所有索引、约束和依赖项删除到需要修改的列,修改该列并再次应用所有索引、约束和依赖项。
这个也试过了。有些索引需要 AGES 才能完成。但是 目前看来是最好的选择 。 在数据类型更改之前应用 ROW COMPRESSION 可能会有变化,但我认为它不会改善实际情况:重新创建索引
- 创建一个具有目标数据类型的新列,从源列复制数据,删除旧列并重命名新列。 此策略还需要删除和重新生成索引,因此它不会提供关于 #2 的很多收益(如果有的话)。
朋友想到了一个变种,就是把列拷贝需要的索引ONLINE复制出来。同时,将源列上的所有更改触发到列复制。
对于上述任何一种策略,都可以通过提高处理能力来获得一些收益。但是,无论如何,我们考虑使用任何一种方法来增加功率,因此这对所有解决方案都是通用的
当您需要一次性更新大量行时,使用以下迁移技术可能更有效:
- 创建新目标table
- 使用 INSERT INTO SELECT 用正确/更新的值填充新的 table
- rename新旧table
- 为新 table 创建索引
经过多次测试和备份,我们最终采用了以下方法:
- 使用所需的格式更改创建新列 [columnName_NEW]。允许 NULLS
- 为 INSERTS 创建触发器以使用要替换的列中的值更新新列
- 批量复制旧列值到新列 这个操作非常耗时。我们 运行 每天维护 window 一批(4 天 2 小时)。我们的批处理首先填充最旧行的值,我们指望触发器填充新行
- #3 完成后,不再允许在新列上使用 NULLS,但设置默认值以避免 INSERT 触发器崩溃
- 在新列上创建所有需要的索引和视图。这非常耗时,但可以在线完成
- 在旧列上允许 NULLS
- 删除插入触发器 - 现在开始停机!
- 将旧列重命名为 [columnName_OLD],将新列重命名为 [columnName]。这需要几秒钟的停机时间!
--> 你可以认为它终于完成了!
- 经过一段安全时间后,您可以备份结果并删除 [columnName_OLD] 及其所有依赖项
我选择了另一个答案,因为我认为它在大多数情况下也很有用。这个有更多的步骤,但停机时间非常短,并且在除最后一步之外的任何步骤都是可逆的。