Timescale Cloud:性能、规模、企业级

自托管产品

MST

本文档提供了详细的分步说明,介绍如何使用双写和回填迁移方法,将数据从使用 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 子句中的授予者必须是当前用户,此子句主要用于 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 用户不是超级用户,不能在所有情况下分配权限。此标志意味着分配给其他用户的权限必须作为手动清理任务在目标数据库中重新分配。这是一个已知限制。

如果源数据库中的 timescaledb 扩展安装在“public”以外的模式中,它会在 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 实例。请使用基于 Linux 的 x86_64 分发版。

准备好将运行 timescaledb-backfill 的实例后,登录并下载 timescaledb-backfill:

wget https://#/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 上编辑此页面