本文档提供了详细的分步说明,指导您使用双写和回填迁移方法,将数据从非 PostgreSQL 的源数据库迁移到 Timescale。

注意

在迁移的上下文中,您现有的生产数据库被称为“源”数据库,而您计划将数据迁移到的新 Timescale 数据库被称为“目标”数据库。

详细来说,迁移过程包括以下步骤

  1. 在 Timescale 中设置目标数据库实例。
  2. 修改应用程序以写入辅助数据库。
  3. 设置模式并将关系数据迁移到目标数据库。
  4. 以双写模式启动应用程序。
  5. 确定完成点 T
  6. 将时序数据从源回填到目标。
  7. 在目标数据库中启用后台作业(策略)。
  8. 验证所有数据都存在于目标数据库中。
  9. 验证目标数据库可以处理生产负载。
  10. 切换应用程序以将目标数据库视为主数据库(可能继续写入源数据库作为备份)。
提示

如果您遇到困难,可以通过打开支持请求获得帮助,或者将您的问题提交到 社区 Slack 中的 #migration 频道,该迁移方法的开发人员将在那里提供帮助。

您可以直接从 Timescale 控制台 或通过电子邮件 support@timescale.com 打开支持请求。

在 Timescale 中创建数据库服务.

如果您计划迁移超过 400 GB 的数据,请打开支持请求以确保在您的 Timescale 实例上预先配置足够的磁盘空间。

您可以直接从 Timescale 控制台 或通过电子邮件 support@timescale.com 打开支持请求。

具体如何执行此操作取决于您的应用程序所用的语言,以及您的数据摄取和应用程序功能的确切方式。在最简单的情况下,您只需并行执行两个插入操作。在一般情况下,您必须考虑如何处理写入源数据库或目标数据库失败的情况,以及您想要或可以构建什么机制来从这种失败中恢复。

如果您的时序数据具有对普通表的外键引用,您必须确保您的应用程序正确维护外键关系。如果被引用的列是 *SERIAL 类型,则插入到源和目标的同一行可能不会获得相同的自动生成 ID。如果发生这种情况,从源回填到目标的数据在内部是不一致的。在最好的情况下,它会导致外键违规,在最坏的情况下,外键约束得到维护,但数据引用了错误的外键。为了避免这些问题,最佳实践是遵循在线迁移

您可能还希望在源数据库和目标数据库上执行相同的读取查询,以评估查询结果的正确性和性能。请记住,目标数据库在一段时间内(可能几天)没有所有数据,因此您应该预期结果在一段时间内不会相同。

描述如何从每个可能的来源迁移数据是不可行的,相反,我们告诉您需要做什么,并希望您找到资源来支持您。

在此步骤中,您需要准备数据库以接收从您的应用程序双写的时序数据。如果您要从另一个时序数据库迁移,那么您只需要担心为将包含时序数据的超表设置模式。有关超表是什么的一些背景知识,请查阅入门指南的表和超表部分。

如果您要从包含关系数据和时序数据的关系数据库迁移,您还需要为关系数据设置模式,并在这一步中复制它,排除任何时序数据。时序数据将在后续步骤中回填。

我们在双写和回填方案中的假设是,关系数据量相对于时序数据而言非常小,因此在您复制关系数据时短暂停止您的生产应用程序不会有问题,或者关系数据更改不频繁,因此您可以在不停止应用程序的情况下获取关系元数据的快照。如果您的应用程序情况并非如此,您应该重新考虑使用双写和回填方法。

注意

如果您计划试验连续聚合,我们建议您首先完成双写和回填迁移,然后再在数据上创建连续聚合。如果您在将数据回填到超表之前在其上创建了连续聚合,则必须刷新整个时间范围的连续聚合,以确保聚合数据中没有漏洞。

设置好目标数据库后,您的应用程序现在可以以双写模式启动。

在双写执行一段时间后,目标超表包含三个时间范围内的数据:缺失写入、延迟到达数据和“一致性”范围

Hypertable dual-write ranges

如果应用程序由多个写入器组成,并且这些写入器并非都同时开始写入目标超表,则会有一段时间内并非所有写入都已进入目标超表。这段时间从第一个写入器开始双写时开始,到最后一个写入器开始双写时结束。

某些应用程序具有延迟到达数据:时间戳在过去,但尚未写入的测量值(例如,来自具有间歇性连接问题的设备)。延迟到达数据窗口介于当前时刻和最大延迟之间。

一致性范围是既没有缺失写入,并且所有数据都已到达的范围,即介于缺失写入范围的结束和延迟到达数据范围的开始之间。

这些范围的长度由应用程序的属性定义,没有一种通用的方法来确定它们是什么。

完成点 T 是在一致性范围内任意选择的时间点。它是可以安全回填数据的点,确保不会丢失数据。

完成点应表示为要回填的超表的 time 列的类型。例如,如果您使用 TIMESTAMPTZ time 列,则完成点可能是 2023-08-10T12:00:00.00Z。如果您使用 BIGINT 列,则它可能是 1695036737000

如果您为超表的 time 列混合使用了类型,则必须分别为每种类型确定完成点,并独立于其他类型的超表集回填每组具有相同类型的超表。

将源数据库中的数据按表转储为 CSV 格式,并使用 timescaledb-parallel-copy 工具将这些 CSV 恢复到目标数据库中。

确定要从源数据库复制到目标数据库的数据窗口。根据源表中的数据量,将源表拆分为多个数据块以独立移动可能是明智的。在以下步骤中,此时间范围称为 <start><end>

通常,time 列的类型为 timestamp with time zone,因此 <start><end> 的值必须类似于 2023-08-01T00:00:00Z。如果 time 列不是 timestamp with time zone,则 <start><end> 的值必须是该列的正确类型。

如果您打算复制源表中的所有历史数据,则 <start> 的值可以是 '-infinity'<end> 值是您确定的完成点 T 的值。

双写过程可能已将数据写入到您要移动的时间范围内的目标数据库中。在这种情况下,必须删除双写的数据。这可以使用 DELETE 语句实现,如下所示

psql $TARGET -c "DELETE FROM <hypertable> WHERE time >= <start> AND time < <end>);"
重要提示

BETWEEN 运算符包含开始和结束范围,因此不建议使用它。

在回填数据时,必须关闭目标超表的压缩策略。这可以防止压缩策略压缩仅半满的数据块。

在以下命令中,将 <hypertable> 替换为目标超表的完全限定表名,例如 public.metrics

psql -d $TARGET -f -v hypertable=<hypertable> - <<'EOF'
SELECT public.alter_job(j.id, scheduled=>false)
FROM _timescaledb_config.bgw_job j
JOIN _timescaledb_catalog.hypertable h ON h.id = j.hypertable_id
WHERE j.proc_schema IN ('_timescaledb_internal', '_timescaledb_functions')
AND j.proc_name = 'policy_compression'
AND j.id >= 1000
AND format('%I.%I', h.schema_name, h.table_name)::text::regclass = :'hypertable'::text::regclass;
EOF

请参阅源数据库的文档,以确定如何将表转储为 CSV。您必须确保 CSV 仅包含完成点之前的数据。您应该在从源数据库转储数据时应用此过滤器。

您可以使用 timescaledb-parallel-copy 将 CSV 文件加载到超表中,如下所示。将工作进程数设置为等于目标数据库中的 CPU 核心数

timescaledb-parallel-copy \
--connection $TARGET \
--table <target_hypertable> \
--workers 8 \
--file <table dump for source table>

上述命令不是事务性的。如果出现连接问题或其他导致复制停止的问题,则必须从目标中删除部分复制的行(使用上面步骤 6b 中的说明),然后可以重新启动复制。

在以下命令中,将 <hypertable> 替换为目标超表的完全限定表名,例如 public.metrics

psql -d $TARGET -f -v hypertable=<hypertable> - <<'EOF'
SELECT public.alter_job(j.id, scheduled=>true)
FROM _timescaledb_config.bgw_job j
JOIN _timescaledb_catalog.hypertable h ON h.id = j.hypertable_id
WHERE j.proc_schema IN ('_timescaledb_internal', '_timescaledb_functions')
AND j.proc_name = 'policy_compression'
AND j.id >= 1000
AND format('%I.%I', h.schema_name, h.table_name)::text::regclass = :'hypertable'::text::regclass;
EOF

现在所有数据都已回填,并且应用程序正在向两个数据库写入数据,两个数据库的内容应该相同。如何最好地验证这一点取决于您的应用程序。

如果您正在并行从两个数据库读取每个生产查询,您可以考虑添加应用程序级别的验证,以验证两个数据库是否返回相同的数据。

另一种选择是比较源表和目标表中的行数,但这会读取表中的所有数据,可能会对您的生产工作负载产生影响。

现在双写已经进行了一段时间,目标数据库应该能够承受生产写入流量。现在是确定目标数据库是否可以处理所有生产流量(包括读取写入)的合适时机。具体如何完成取决于应用程序,由您自行决定。

一旦您验证了所有数据都存在,并且目标数据库可以处理生产工作负载,最后一步是将目标数据库切换为您的主数据库。您可能希望在一段时间内继续写入源数据库,直到您确定目标数据库可以承受所有生产流量。

关键词

在此页面上发现问题?报告问题 或 在 GitHub 上编辑此页面