本页面介绍了如何将 NoSQL 数据库从 Cassandra 迁移到 Spanner。
Cassandra 和 Spanner 都是为需要高可伸缩性和低延迟的应用而构建的大规模分布式数据库。虽然这两种数据库都可以支持要求苛刻的 NoSQL 工作负载,但 Spanner 提供了数据建模、查询和事务操作的高级功能。Spanner 支持 Cassandra 查询语言 (CQL)。
如需详细了解 Spanner 如何满足 NoSQL 数据库条件,请参阅适用于非关系型工作负载的 Spanner。
迁移限制
如需成功从 Cassandra 迁移到 Spanner 上的 Cassandra 端点,请查看适用于 Cassandra 用户的 Spanner,了解 Spanner 架构、数据模型和数据类型与 Cassandra 的区别。 在开始迁移之前,请仔细考虑 Spanner 和 Cassandra 之间的功能差异。
迁移过程
迁移过程分为以下步骤:
- 转换架构和数据模型。
- 为传入数据设置双重写入。
- 将历史数据从 Cassandra 批量导出到 Spanner。
- 验证数据以确保整个迁移过程的数据完整性。
- 将应用指向 Spanner 而不是 Cassandra。
- 可选。执行从 Spanner 到 Cassandra 的反向复制。
转换架构和数据模型
将数据从 Cassandra 迁移到 Spanner 的第一步是将 Cassandra 数据架构调整为 Spanner 架构,同时处理数据类型和建模方面的差异。
Cassandra 和 Spanner 的表声明语法非常相似。您可以指定表名称、列名称和类型,以及唯一标识行的主键。主要区别在于,Cassandra 采用哈希分区,并区分主键的两个部分:哈希分区键和排序聚类列,而 Spanner 采用范围分区。您可以将 Spanner 的主键视为仅具有聚簇列,并在后台自动维护分区。与 Cassandra 一样,Spanner 也支持复合主键。
我们建议您按照以下步骤将 Cassandra 数据架构转换为 Spanner:
- 查看 Cassandra 概览,了解 Cassandra 和 Spanner 数据架构之间的异同,以及如何映射不同的数据类型。
- 使用 Cassandra 到 Spanner 架构工具提取 Cassandra 数据架构并将其转换为 Spanner。
- 在开始数据迁移之前,确保已使用适当的数据架构创建 Spanner 表。
为传入的数据设置实时迁移
如需执行从 Cassandra 到 Spanner 的零停机时间迁移,请为传入数据设置实时迁移。实时迁移侧重于通过使用实时复制来尽可能缩短停机时间并确保应用持续可用。
在批量迁移之前,先从实时迁移过程开始。下图显示了实时迁移的架构视图。
实时迁移架构具有以下关键组件:
- 源:您的源 Cassandra 数据库。
- 目标:您要迁移到的目标 Spanner 数据库。假设您已使用与 Cassandra 架构兼容的架构(并针对 Spanner 的数据模型和功能进行了必要的调整)配置 Spanner 实例和数据库。
DataStax ZDM 代理:ZDM 代理是由 DataStax 为 Cassandra 到 Cassandra 迁移而构建的双重写入代理。代理模拟 Cassandra 集群,让应用无需更改即可使用代理。此工具是您的应用与之对话并在内部用于对源数据库和目标数据库执行双重写入的工具。虽然它通常与 Cassandra 集群搭配用作源和目标,但我们的设置会将其配置为使用 Cassandra-Spanner 代理(作为边车运行)作为目标。这可确保每项传入读取仅转发到源,并将源响应返回给应用。此外,每个传入写入都会同时定向到源和目标。
- 如果对源和目标的写入都成功,应用会收到成功消息。
- 如果写入源失败,但写入目标成功,应用会收到源的失败消息。
- 如果写入目标失败,但写入源成功,应用会收到目标的失败消息。
- 如果对源和目标的写入均失败,应用会收到源的失败消息。
Cassandra-Spanner 代理:一种边车应用,可拦截面向 Cassandra 的 Cassandra 查询语言 (CQL) 流量,并将其转换为 Spanner API 调用。它允许应用和工具使用 Cassandra 客户端与 Spanner 进行互动。
客户端应用:用于读取和向源 Cassandra 集群写入数据的应用。
代理设置
执行实时迁移的第一步是部署和配置代理。Cassandra-Spanner 代理作为 ZDM 代理的边车运行。边车代理充当 ZDM 代理向 Spanner 执行写入操作的目标。
使用 Docker 进行单个实例测试
您可以在本地或虚拟机上运行代理的单个实例,以使用 Docker 进行初始测试。
前提条件
- 确认代理运行的虚拟机与应用、源 Cassandra 数据库和 Spanner 数据库之间具有网络连接。
- 安装 Docker。
- 确认存在具有向 Spanner 实例和数据库写入数据所需权限的服务账号密钥文件。
- 设置 Spanner 实例、数据库和架构。
- 确保 Spanner 数据库名称与源 Cassandra 键空间名称相同。
- 克隆 spanner-migration-tool 代码库。
下载并配置 ZDM 代理
- 转到
sources/cassandra
目录。 - 确保
entrypoint.sh
和Dockerfile
文件与 Dockerfile 位于同一目录中。 运行以下命令以构建本地映像:
docker build -t zdm-proxy:latest .
运行 ZDM 代理
- 确保在运行以下命令时本地存在
zdm-config.yaml
和keyfiles
。 - 打开示例 zdm-config yaml 文件。
- 查看 ZDM 接受的详细标志列表。
使用以下命令运行容器:
sudo docker run --restart always -d -p 14002:14002 \ -v zdm-config-file-path:/zdm-config.yaml \ -v local_keyfile:/var/run/secret/keys.json \ -e SPANNER_PROJECT=SPANNER_PROJECT_ID \ -e SPANNER_INSTANCE=SPANNER_INSTANCE_ID \ -e SPANNER_DATABASE=SPANNER_DATABASE_ID \ -e GOOGLE_APPLICATION_CREDENTIALS="/var/run/secret/keys.json" \ -e ZDM_CONFIG=/zdm-config.yaml \ zdm-proxy:latest
验证代理设置
使用
docker logs
命令检查代理日志,了解在启动期间是否发生任何错误:docker logs container-id
运行
cqlsh
命令以验证是否已正确设置代理:cqlsh VM-IP 14002
将 VM-IP 替换为虚拟机的 IP 地址。
使用 Terraform 设置生产环境:
对于生产环境,我们建议使用提供的 Terraform 模板来编排 Cassandra-Spanner 代理的部署。
前提条件
- 安装 Terraform。
- 确认应用具有创建资源所需的适当权限的默认凭证。
- 确认服务密钥文件具有向 Spanner 写入数据的相关权限。代理使用此文件。
- 设置 Spanner 实例、数据库和架构。
- 确认 Dockerfile、
entrypoint.sh
和服务密钥文件与main.tf
文件位于同一目录中。
配置 Terraform 变量
- 确保您拥有用于代理部署的 Terraform 模板。
- 使用您的设置的变量更新
terraform.tfvars
文件。
使用 Terraform 部署模板
Terraform 脚本会执行以下操作:
- 根据指定的数量创建容器优化型虚拟机。
- 为每个虚拟机创建
zdm-config.yaml
文件,并为其分配拓扑索引。ZDM 代理需要多虚拟机设置才能使用配置yaml
文件中的PROXY_TOPOLOGY_ADDRESSES
和PROXY_TOPOLOGY_INDEX
字段来配置拓扑。 - 将相关文件传输到每个虚拟机,远程运行 Docker 构建,并启动容器。
如需部署模板,请执行以下操作:
使用
terraform init
命令初始化 Terraform:terraform init
运行
terraform plan
命令,以查看 Terraform 计划对您的基础架构进行哪些更改:terraform plan -var-file="terraform.tfvars"
当资源看起来不错时,运行
terraform apply
命令:terraform apply -var-file="terraform.tfvars"
在 Terraform 脚本停止后,运行
cqlsh
命令以确保虚拟机可访问。cqlsh VM-IP 14002
将 VM-IP 替换为虚拟机的 IP 地址。
将您的客户端应用指向 ZDM 代理
修改客户端应用的配置,将联系点设置为运行代理的虚拟机,而不是原始 Cassandra 集群。
全面测试您的应用。验证写入操作是否同时应用于源 Cassandra 集群,并通过检查 Spanner 数据库来验证它们是否也使用 Cassandra-Spanner 代理访问 Spanner。读取由源 Cassandra 提供。
将数据批量导出到 Spanner
批量数据迁移涉及在数据库之间传输大量数据,通常需要仔细规划和执行,以最大限度地减少停机时间并确保数据完整性。这些方法包括 ETL(提取、转换、加载)流程、直接数据库复制和专用迁移工具,所有这些方法都旨在高效地移动数据,同时保留其结构和准确性。
我们建议您使用 Spanner 的 SourceDB To Spanner Dataflow 模板将数据从 Cassandra 批量迁移到 Spanner。Dataflow 是 Google Cloud 分布式提取、转换和加载 (ETL) 服务,可为运行数据流水线以在多台机器上并行读取和处理大量数据提供了一个平台。SourceDB To Spanner Dataflow 模板旨在从 Cassandra 执行高度并行化的读取,根据需要转换源数据,并将数据写入 Spanner 作为目标数据库。
使用 Cassandra 配置文件执行从 Cassandra 批量迁移到 Spanner 说明中的步骤。
验证数据以确保完整性
在数据库迁移期间进行数据验证对于确保数据准确性和完整性至关重要。其涉及比较源 Cassandra 和目标 Spanner 数据库之间的数据,以识别差异,例如缺失、损坏或不匹配的数据。常规数据验证方法包括校验和、行数和详细数据比较,所有这些方法都旨在保证迁移的数据能够准确表示原始数据。
批量数据迁移完成后,在双重写入仍处于活跃状态时,您需要验证数据一致性并修正差异。出于各种原因,Cassandra 和 Spanner 之间的差异可能会在双重写入阶段发生,包括:
- 双重写入失败。写入操作在一个数据库中可能成功,但由于暂时性网络问题或其他错误,而在另一个数据库中可能失败。
- 轻量级事务 (LWT)。如果您的应用使用 LWT(比较和设置)操作,这些操作可能会在一个数据库中成功,但由于数据集存在差异,而在另一个数据库中失败。
- 单个主键的每秒查询次数 (QPS) 较高。如果对同一分区键进行写入负载非常高,由于网络往返时间不同,源和目标之间的事件顺序可能会有所不同,从而可能导致不一致。
批量作业和双重写入并行运行:与双重写入并行运行的批量迁移可能会因各种竞态条件而导致数据不一致,例如以下情况:
- Spanner 上的额外行:如果在双重写入处于活跃状态时运行批量迁移,应用可能会删除已被批量迁移作业读取且写入目标位置的行。
- 批量写入和双重写入之间的竞态条件:可能会出现其他各种竞态条件,例如,当双重写入完成后,批量写入作业从 Cassandra 读取一行,而该行中的数据在传入的写入更新 Spanner 上的行时过时。
- 部分列更新:更新现有行中的部分列会在 Spanner 上创建一个条目,其中其他列为 null。由于批量更新不会覆盖现有行,因此会导致行在 Cassandra 和 Spanner 之间不一致。
此步骤侧重于在源数据库和目标数据库之间验证和协调数据。验证涉及比较源和目标以识别不一致之处,而协调侧重于解决这些不一致之处以实现数据一致性。
比较 Cassandra 和 Spanner 之间的数据
我们建议您对行数和行实际内容都执行验证。
选择如何比较数据(计数和行匹配)取决于您的应用对数据不一致的容忍度以及您对精确验证的要求。
您可以通过以下两种方式验证数据:
在双重写入处于活跃状态时,执行活跃验证。在这种情况下,数据库中的数据仍在更新。Cassandra 和 Spanner 之间的行数或行内容可能无法完全匹配。目的是确保任何差异仅是因数据库上的活跃负载而导致,而不是因任何其他错误而导致。如果差异在这些限制范围内,您可以继续进行割接。
静态验证需要停机。如果您的要求需要强大的静态验证,并保证数据的准确一致性,您可能需要暂时停止对这两个数据库的所有写入。然后,您可以验证数据并在 Spanner 数据库中协调差异。
根据您对数据一致性和可接受的停机时间的具体要求,选择验证时间和适当的工具。
比较 Cassandra 和 Spanner 中的行数
一种数据验证方法是比较源数据库和目标数据库中表中的行数。您可以通过以下几种方式执行计数验证:
如果使用小型数据集(每张表少于 1 千万行)进行迁移,您可以使用此匹配计数脚本来计算 Cassandra 和 Spanner 中的行数。此方法可在短时间内返回确切的计数。Cassandra 中的默认超时为 10 秒。如果脚本在完成计数之前超时,请考虑增加驱动程序请求超时和服务器端超时。
迁移大型数据集(每张表超过 1 千万行)时,请注意,虽然 Spanner 计数查询可以很好地扩缩,但 Cassandra 查询往往会超时。在这种情况下,我们建议您使用 DataStax Bulk Loader 工具从 Cassandra 表中获取行数。对于 Spanner 计数,使用 SQL
count(*)
函数就足以满足大多数大规模加载的需求。我们建议您针对每个 Cassandra 表运行 Bulk Loader,并从 Spanner 表中提取计数,然后将两者进行比较。您可以手动执行此操作,也可以使用脚本执行。
验证行是否匹配
我们建议您比较源数据库和目标数据库中的行,以识别行之间的不匹配项。您可以通过两种方式执行行验证。 您使用的方式取决于应用的要求:
- 验证一组随机的行。
- 验证整个数据集。
验证行的随机样本
对于大型工作负载,验证整个数据集既昂贵又耗时。在这些情况下,您可以使用抽样来验证数据的随机子集,以检查行中是否存在不匹配项。实现此目的的一种方法是在 Cassandra 中随机选择行,并在 Spanner 中提取相应行,然后比较值(或行哈希)。
这种方法的优点是比检查整个数据集更快完成,并且运行起来很简单。缺点是,由于它是数据的子集,因此对于边缘情况,数据仍可能存在差异。
如需从 Cassandra 中随机抽取行,您需要执行以下操作:
- 生成令牌范围 [
-2^63
、2^63 - 1
] 内的随机号码。 - 提取行
WHERE token(PARTITION_KEY) > GENERATED_NUMBER
。
validation.go
示例脚本会随机提取行,并使用 Spanner 数据库中的行对其进行验证。
验证整个数据集
如需验证整个数据集,请提取原始 Cassandra 数据库中的所有行。使用主键提取所有对应的 Spanner 数据库行。然后,您可以比较行以查看差异。对于大型数据集,您可以使用基于 MapReduce 的框架(例如 Apache Spark 或 Apache Beam),来可靠且高效地验证整个数据集。
这样做的好处是,完整验证可提高数据一致性的置信度。缺点是,它增加了 Cassandra 上的读取负载,并且需要投入资源来为大型数据集构建复杂的工具。在大型数据集上完成验证可能需要更长时间。
实现此目的的一种方法是,对令牌范围进行分区,且并行查询 Cassandra 环。对于每个 Cassandra 行,系统都会使用分区键提取等效的 Spanner 行。然后,系统会比较这两行数据是否存在差异。如需了解构建验证器作业时要遵循的指针,请参阅使用行匹配验证 Cassandra 的技巧。
协调数据或行数不一致
根据数据一致性要求,您可以将行从 Cassandra 复制到 Spanner,以协调验证阶段发现的差异。进行协调的一种方法是扩展用于完整数据集验证的工具,并在发现不匹配时将正确的行从 Cassandra 复制到目标 Spanner 数据库。如需了解详情,请参阅实现方面的注意事项。
将应用指向 Spanner 而不是 Cassandra
在验证迁移后数据的准确性和完整性后,选择一个时间点将应用迁移为指向 Spanner 而不是 Cassandra(或指向用于实时数据迁移的代理适配器)。这称为切换。
如需执行切换,请按照以下步骤操作:
为客户端应用创建配置更改,以便它可以使用以下方法之一直接连接到 Spanner 实例:
- 将 Cassandra 连接到以边车形式运行的 Cassandra 适配器。
- 使用端点客户端更改驱动程序 JAR。
应用您在上一步中准备的更改,以将您的应用指向 Spanner。
为您的应用设置监控,以监控错误或性能问题。使用 Cloud Monitoring 监控 Spanner 指标。如需了解详情,请参阅使用 Cloud Monitoring 监控实例。
在成功切换并稳定运行后,停用 ZDM 代理和 Cassandra-Spanner 代理实例。
执行从 Spanner 到 Cassandra 的反向复制
您可以使用 Spanner to
SourceDB
Dataflow 模板执行反向复制。在遇到 Spanner 的不可预见的问题并需要以尽可能少地中断服务的方式回退到原始 Cassandra 数据库时,反向复制非常有用。
使用行匹配验证 Cassandra 的技巧
使用 SELECT *
在 Cassandra(或任何其他数据库)中执行完整表扫描会非常慢且效率低下。如需解决此问题,请将 Cassandra 数据集划分为可管理的分区,且并发处理这些分区。为此,您需要执行以下操作:
将数据集拆分成令牌范围
Cassandra 会根据分区键令牌在各个节点之间分配数据。
Cassandra 集群的令牌范围从 -2^63
到 2^63 -
1
。您可以定义固定数量的大小相等的令牌范围,以将整个键空间划分为较小的分区。我们建议您使用可配置的 partition_size
参数来拆分令牌范围,以便您可以调整该参数以快速处理整个范围。
并行查询分区
定义令牌范围后,您可以启动多个并行进程或线程,每个进程或线程负责验证特定范围内的数据。对于每个范围,您都可以对分区键 (pk
) 使用 token
函数构建 CQL 查询。
针对给定令牌范围的示例查询如下所示:
SELECT *
FROM your_keyspace.your_table
WHERE token(pk) >= partition_min_token AND token(pk) <= partition_max_token;
通过迭代已定义的令牌范围并针对您的原始 Cassandra 集群(或通过配置为从 Cassandra 读取的 ZDM 代理)并行执行这些查询,您可以以分布式方式高效读取数据。
读取每个分区中的数据
每个并行进程都会执行基于范围的查询,并从 Cassandra 中检索数据的子集。检查检索到的分区数据量,以确保并行处理和内存用量之间保持平衡。
从 Spanner 中提取相应行
对于从 Cassandra 提取的每行,请使用源行键从目标 Spanner 数据库中检索相应行。
比较行以识别不匹配项
在获得 Cassandra 行和相应的 Spanner 行(如果存在)后,您需要比较它们的字段,以识别任何不匹配项。这种比较应考虑潜在的数据类型差异以及迁移期间应用的任何转换。我们建议您根据应用的要求,定义构成不匹配的明确标准。
设计可扩展性的验证工具
设计验证工具时,应考虑扩展以进行协调的可能性。例如,您可以添加功能,以将正确的数据从 Cassandra 写入 Spanner,以便针对已识别的不匹配项进行处理。
报告和日志不匹配
我们建议您记录任何已识别的不匹配项,并提供足够的上下文信息,以便进行调查和协调。这可能包括主键、不同的特定字段以及 Cassandra 和 Spanner 中的值。您可能还需要汇总有关发现的不匹配项数量和类型的统计信息。
启用和停用 Cassandra 数据的 TTL
本部分介绍了如何在 Spanner 表中针对 Cassandra 数据启用和停用存留时间 (TTL)。如需查看概览,请参阅存留时间 (TTL)。
在 Cassandra 数据上启用 TTL
对于本部分中的示例,假设您有一个具有以下架构的表:
CREATE TABLE Singers (
SingerId INT64 OPTIONS (cassandra_type = 'bigint'),
AlbumId INT64 OPTIONS (cassandra_type = 'int'),
) PRIMARY KEY (SingerId);
如需在现有表上启用行级 TTL,请执行以下操作:
添加时间戳列,用于存储每行的到期时间戳。 在此示例中,列名为
ExpiredAt
,但您可以使用任何名称。ALTER TABLE Singers ADD COLUMN ExpiredAt TIMESTAMP;
添加行删除政策,以自动删除保留时间超过过期时间的行。
INTERVAL 0 DAY
表示在达到过期时间后立即删除相应行。ALTER TABLE Singers ADD ROW DELETION POLICY (OLDER_THAN(ExpiredAt, INTERVAL 0 DAY));
将
cassandra_ttl_mode
设置为row
以启用行级 TTL。ALTER TABLE Singers SET OPTIONS (cassandra_ttl_mode = 'row');
(可选)设置
cassandra_default_ttl
以配置默认 TTL 值。 此值以秒为单位。ALTER TABLE Singers SET OPTIONS (cassandra_default_ttl = 10000);
停用 Cassandra 数据上的 TTL
对于本部分中的示例,假设您有一个具有以下架构的表:
CREATE TABLE Singers (
SingerId INT64 OPTIONS ( cassandra_type = 'bigint' ),
AlbumId INT64 OPTIONS ( cassandra_type = 'int' ),
ExpiredAt TIMESTAMP,
) PRIMARY KEY (SingerId),
ROW DELETION POLICY (OLDER_THAN(ExpiredAt, INTERVAL 0 DAY)), OPTIONS (cassandra_ttl_mode = 'row');
如需在现有表上停用行级 TTL,请执行以下操作:
(可选)将
cassandra_default_ttl
设置为零以清理默认 TTL 值。ALTER TABLE Singers SET OPTIONS (cassandra_default_ttl = 0);
将
cassandra_ttl_mode
设置为none
可停用行级 TTL。ALTER TABLE Singers SET OPTIONS (cassandra_ttl_mode = 'none');
移除行删除政策。
ALTER TABLE Singers DROP ROW DELETION POLICY;
移除过期时间戳列。
ALTER TABLE Singers DROP COLUMN ExpiredAt;
实现方面的注意事项
- 框架和库:如需进行可扩展的自定义验证,请使用基于 MapReduce 的框架,例如 Apache Spark 或 Dataflow (Beam)。选择一种受支持的语言(Python、Scala、Java),并使用用于 Cassandra 和 Spanner 的连接器,例如使用代理。 这些框架可高效地并行处理大型数据集,以进行全面验证。
- 错误处理和重试:实施强大的错误处理来管理潜在问题,例如网络连接问题或任一数据库暂时不可用。考虑针对暂时性故障实施重试机制。
- 配置:使令牌范围、两个数据库的连接详细信息和比较逻辑可配置。
- 性能调优:通过实验确定并行进程的数量和令牌范围的大小,以针对您的特定环境和数据量优化验证流程。在验证期间监控 Cassandra 和 Spanner 集群上的负载。
后续步骤
- 请参阅 Cassandra 概览,了解 Spanner 与 Cassandra 之间的比较。
- 了解如何使用 Cassandra 适配器连接到 Spanner。