本文档提供了详细的分步说明,指导您使用双写和回填迁移方法,将数据从使用 TimescaleDB 的源数据库迁移到 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。如果发生这种情况,从源回填到目标的数据在内部是不一致的。在最好的情况下,它会导致外键违规,在最坏的情况下,外键约束得到维护,但数据引用了错误的外键。为了避免这些问题,最佳实践是遵循在线迁移

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

本节利用 pg_dumpallpg_dump 将您在源数据库中使用的角色和关系模式迁移到目标数据库。

重要提示

源数据库和目标数据库的 PostgreSQL 版本可以不同,只要目标版本高于源版本即可。

两个数据库中使用的 TimescaleDB 版本必须完全相同。

注意

为了方便起见,在本指南中,到源数据库和目标数据库的连接字符串分别称为 $SOURCE$TARGET。这可以在您的 shell 中设置,例如

export SOURCE="postgres://<user>:<password>@<source host>:<source port>/<db_name>"
export TARGET="postgres://<user>:<password>@<target host>:<target port>/<db_name>"
pg_dumpall -d "$SOURCE" \
-l $DB_NAME \
--quote-all-identifiers \
--roles-only \
--file=roles.sql

Timescale 服务不支持具有超级用户访问权限的角色。如果您的 SQL 转储包含具有此类权限的角色,您需要修改该文件以符合安全模型。

您可以使用以下 sed 命令从您的 roles.sql 文件中删除不支持的语句和权限

sed -i -E \
-e '/CREATE ROLE "postgres";/d' \
-e '/ALTER ROLE "postgres"/d' \
-e '/CREATE ROLE "tsdbadmin";/d' \
-e '/ALTER ROLE "tsdbadmin"/d' \
-e 's/(NO)*SUPERUSER//g' \
-e 's/(NO)*REPLICATION//g' \
-e 's/(NO)*BYPASSRLS//g' \
-e 's/GRANTED BY "[^"]*"//g' \
roles.sql
注意

此命令仅适用于 GNU sed 实现(有时称为 gsed)。对于 BSD 实现(macOS 上的默认实现),您需要添加一个额外的参数来将 -i 标志更改为 -i ''

要检查 sed 版本,您可以使用命令 sed --version。虽然 GNU 版本明确标识自己为 GNU,但 BSD 版本的 sed 通常不提供直接的 --version 标志,而只是输出“非法选项”错误。

以下是此脚本的简要说明

  • CREATE ROLE "postgres"; 和 ALTER ROLE "postgres":这些语句被删除是因为它们需要超级用户访问权限,而 Timescale 不支持超级用户访问权限。

  • (NO)SUPERUSER | (NO)REPLICATION | (NO)BYPASSRLS:这些是需要超级用户访问权限的权限。

  • GRANTED BY role_specification:GRANTED BY 子句也可能具有需要超级用户访问权限的权限,因此应将其删除。注意:根据 TimescaleDB 文档,GRANTED BY 子句中的 GRANTOR 必须是当前用户,并且此子句主要用于 SQL 兼容性。因此,可以安全地删除它。

pg_dump -d "$SOURCE" \
--format=plain \
--quote-all-identifiers \
--no-tablespaces \
--no-owner \
--no-privileges \
--exclude-table-data='_timescaledb_internal.*' \
--file=dump.sql
  • --exclude-table-data='_timescaledb_internal.*' 导出超表块的结构,但不导出数据。这将在目标上创建空块,为回填过程做好准备。
  • 需要 --no-tablespaces,因为 Timescale 不支持除默认表空间之外的其他表空间。这是一个已知的限制。

  • 需要 --no-owner,因为 Timescale 的 tsdbadmin 用户不是超级用户,并且在所有情况下都无法分配所有权。此标志意味着所有内容都归用于连接到目标的用户所有,而与源中的所有权无关。这是一个已知的限制。

  • 需要 --no-privileges,因为 Timescale 的 tsdbadmin 用户不是超级用户,并且在所有情况下都无法分配权限。此标志意味着分配给其他用户的权限必须在目标数据库中作为手动清理任务重新分配。这是一个已知的限制。

如果源数据库在“public”以外的模式中安装了 timescaledb 扩展,则会在 Timescale 上引起问题。编辑转储文件以删除对非公共模式的任何引用。扩展必须位于 Timescale 上的“public”模式中。这是一个已知的限制。

源数据库和目标数据库中的 TimescaleDB 扩展版本完全相同非常重要。这需要在迁移之前升级源数据库中的 TimescaleDB 扩展。

您可以使用以下命令确定目标数据库中 TimescaleDB 的版本

psql $TARGET -c "SELECT extversion FROM pg_extension WHERE extname = 'timescaledb';"

要更新源数据库中的 TimescaleDB 扩展,首先确保从您的软件包存储库中安装了所需的版本。然后,您可以使用以下查询升级扩展

psql $SOURCE -c "ALTER EXTENSION timescaledb UPDATE TO '<version here>';"

有关更多信息和指导,请查阅 升级 TimescaleDB 页面。

psql -X -d "$TARGET" \
-v ON_ERROR_STOP=1 \
--echo-errors \
-f roles.sql \
-c 'select public.timescaledb_pre_restore();' \
-f dump.sql \
-f - <<'EOF'
begin;
select public.timescaledb_post_restore();
-- disable all background jobs
select public.alter_job(id::integer, scheduled=>false)
from _timescaledb_config.bgw_job
where id >= 1000
;
commit;
EOF
注意

关闭后台作业是为了防止连续聚合刷新作业使用不完整/缺失的数据更新连续聚合。一旦迁移完成,必须在所需范围内手动更新连续聚合。

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

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

Hypertable dual-write ranges

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

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

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

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

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

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

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

从 TimescaleDB 回填的最简单方法是使用 timescaledb-backfill 回填工具。它可以有效地将压缩和未压缩的超表以及存储在连续聚合中的数据从一个数据库复制到另一个数据库。

当从靠近目标数据库的机器执行时,timescaledb-backfill 性能最佳。理想的情况是位于与 Timescale 服务相同区域的 EC2 实例。在 x86_64 上使用基于 Linux 的发行版。

在将运行 timescaledb-backfill 的实例准备就绪后,登录并下载 timescaledb-backfill

wget https://assets.timescale.com/releases/timescaledb-backfill-x86_64-linux.tar.gz
tar xf timescaledb-backfill-x86_64-linux.tar.gz
sudo mv timescaledb-backfill /usr/local/bin/

运行 timescaledb-backfill 是一个四阶段过程

  1. 阶段:此步骤准备有关要在目标数据库中复制的数据的元数据。完成后,它会输出要复制的块数。
    timescaledb-backfill stage --source $SOURCE --target $TARGET --until <completion point>
  2. 复制:此步骤按块逐个地将数据从源复制到目标。如果它失败或中断,可以安全地恢复。您应该注意 --parallelism 参数,该参数决定了用于复制数据的连接数。默认值为 8,根据您的源数据库和目标数据库的大小,该值可能过高或过低。您应该密切观察源数据库的性能并相应地调整此参数。
    timescaledb-backfill copy --source $SOURCE --target $TARGET
  3. 验证(可选):此步骤验证源和目标中的数据是否相同。它按块逐个地从源数据库和目标数据库读取所有数据,因此也可能影响源数据库的性能。
    timescaledb-backfill verify --source $SOURCE --target $TARGET
  4. 清理:此步骤删除由 stage 命令在目标数据库中创建的元数据。
    timescaledb-backfill clean --target $TARGET

在启用作业之前,请验证是否存在任何连续聚合刷新策略。

psql -d $TARGET \
-c "select count(*)
from _timescaledb_config.bgw_job
where proc_name = 'policy_refresh_continuous_aggregate'"

如果存在,请在重新启用作业之前刷新连续聚合。timescaledb-backfill 工具提供了一个实用程序来执行此操作

timescaledb-backfill refresh-caggs --source $SOURCE --target $TARGET

更新连续聚合后,您可以重新启用所有后台作业

psql -d $TARGET -f - <<EOF
select public.alter_job(id::integer, scheduled=>true)
from _timescaledb_config.bgw_job
where id >= 1000;
EOF
注意

如果回填过程花费的时间足够长,以至于需要完成大量的保留/压缩工作,则最好手动运行作业以控制工作节奏,直到它赶上进度后再重新启用。

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

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

另一种选择是比较源表和目标表中的行数,尽管这会读取表中的所有数据,这可能会对您的生产工作负载产生影响。timescaledb-backfillverify 子命令执行此检查。

另一种选择是在源表和目标表上运行 ANALYZE,然后按块逐个地查看 pg_class 表的 reltuples 列。结果不精确,但不需要从表中读取所有行。

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

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

关键词

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