【运维】StarRocks 跨集群数据迁移工具 - 基于快照进行的快速迁移

来源:反编译自 starrocks-cluster-sync-2.0-jar-with-dependencies.jar
反编译工具:CFR 0.152
主类com.starrocks.sync.SyncJob


Starrocks-跨集群数据迁移工具

一、整体迁移逻辑

1.1 架构概览

StarRocks 跨集群数据迁移工具采用多线程协调架构,通过周期性元数据同步任务队列管理实现自动化数据迁移。

┌─────────────────────────────────────────────────────────────┐
│                    迁移工具(客户端)                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │ meta-handler │  │ ddl-handler  │  │replication-  │     │
│  │              │  │              │  │job-handler   │     │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘     │
│         │                 │                 │              │
│         └─────────────────┼─────────────────┘              │
│                           │                                 │
│                    ┌──────▼───────┐                        │
│                    │sync-reporter │                        │
│                    └──────────────┘                        │
└───────────────────────────┬─────────────────────────────────┘
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
        ▼                   ▼                   ▼
┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  源集群 FE   │    │  目标集群 FE │    │  目标集群 BE │
│  (元数据查询)│    │  (RPC 调用)  │    │  (数据接收)  │
└──────┬───────┘    └──────────────┘    └──────┬───────┘
       │                                        │
       └────────────────────────────────────────┘
                    │ HTTP 快照传输
                    ▼
            ┌──────────────┐
            │  源集群 BE   │
            │  (数据提供)  │
            └──────────────┘

1.2 核心工作流程

阶段 1:初始化
SyncJob() {
    1. 读取配置文件(sync.properties, hosts.properties)
    2. 验证必要配置(source_cluster_token 等)
    3. 初始化任务队列
    4. 创建 ClusterMetaKeeper(元数据管理器)
}
阶段 2:启动工作线程
start() {
    启动 4 个工作线程:
    
    1. meta-handler(元数据处理线程)
       - 周期性更新源集群和目标集群的元数据
       - 生成 DDL 任务
       - 生成数据复制任务
    
    2. ddl-handler(DDL 执行线程)
       - 从 DDL 队列取出任务
       - 批量执行 DDL(CREATE/DROP TABLE/PARTITION 等)
    
    3. replication-job-handler(数据复制线程)
       - 从复制任务队列取出任务
       - 通过 RPC 发送到目标集群 FE
       - 更新任务状态
    
    4. sync-reporter(进度报告线程)
       - 查询事务状态
       - 统计任务进度
       - 输出日志报告
}
阶段 3:周期性执行
循环执行(直到手动停止):
  ├─ meta-handler: 更新元数据 → 生成任务
  ├─ ddl-handler: 执行 DDL 任务
  ├─ replication-job-handler: 发送复制任务
  └─ sync-reporter: 报告进度

1.3 任务生成逻辑

1.3.1 元数据同步
updateClusterMeta() {
    并行执行:
    1. 同步源集群元数据
       - FE 节点信息
       - BE 节点信息
       - 数据库、表、分区、索引信息
       - 版本信息
    
    2. 同步目标集群元数据
       - FE 节点信息
       - BE 节点信息
       - 数据库、表、分区、索引信息
       - 版本信息
       - 事务状态(运行中/已完成)
}
1.3.2 DDL 任务生成
produceDDL() {
    比较源集群和目标集群的元数据差异:
    
    1. handleDbDDL()
       - 源集群有,目标集群没有 → 创建数据库
       - 目标集群有,源集群没有 → 删除数据库(可选)
    
    2. handleTableDDL()
       - 源集群有,目标集群没有 → 创建表
       - 目标集群有,源集群没有 → 删除表(可选)
       - 表结构不一致 → 删除并重建(可选)
    
    3. handlePartitionDDL()
       - 源集群有,目标集群没有 → 添加分区
       - 目标集群有,源集群没有 → 删除分区(可选)
       - 版本不一致 → 删除并重建(可选)
    
    4. handleMaterializedViewDDL()
       - 同步物化视图结构(不同步数据)
    
    5. handleViewDDL()
       - 同步视图定义
}
1.3.3 数据复制任务生成
produceReplicationJob() {
    遍历公共数据库和表:
    
    for (数据库 in 公共数据库) {
        for (表 in 公共表) {
            for (分区 in 公共分区) {
                1. 比较分区版本
                   - 源版本 > 目标版本 → 需要同步
                   - 源版本 = 目标版本 → 检查版本时间(存算分离)
                   - 源版本 < 目标版本 → 删除目标分区
                
                2. 收集需要同步的分区信息
                   - 分区 ID
                   - 源版本号
                   - 索引信息
                   - Tablet 映射关系
                   - 源 BE 节点信息
                
                3. 检查数据量限制
                   - 如果超过 max_replication_data_size_per_job_in_gb
                   - 停止添加分区,标记为部分复制
            }
            
            4. 创建 ReplicationJob
               - 包含所有需要同步的分区
               - 添加到复制任务队列
        }
    }
}

1.4 任务执行流程

1.4.1 DDL 任务执行
DDL 任务队列
    ↓
批量取出(ddlJobBatchSize)
    ↓
执行 SQL(CREATE/DROP/ALTER)
    ↓
更新元数据
1.4.2 数据复制任务执行
复制任务队列
    ↓
批量取出(replicationJobBatchSize)
    ↓
转换为 Thrift 请求
    ↓
RPC 调用目标集群 FE
    ↓
目标 FE 创建事务并协调 BE
    ↓
目标 BE 从源 BE 拉取数据
    ↓
查询事务状态跟踪进度

1.5 状态跟踪机制

1.5.1 任务状态
JobState {
    UNKNOWN,        // 未知状态
    INIT,           // 已发送到目标集群
    SENT,           // 已发送但未开始运行
    SENT_FAILED,    // 发送失败
    RUNNING,        // 正在运行
    FAILED,         // 失败
    FINISHED        // 完成
}
1.5.2 状态查询
getTxnStatus(jobToken) {
    1. 解析 jobToken → {dbId}-{tableId}_{jobId}
    
    2. 查询运行中的事务
       SHOW PROC '/transactions/{dbId}/running'
       - 如果找到 → RUNNING
    
    3. 查询已完成的事务
       SHOW PROC '/transactions/{dbId}/finished'
       - 如果找到且状态为 VISIBLE → FINISHED
       - 如果找到且状态为 ABORTED → FAILED
    
    4. 都找不到 → UNKNOWN
}

二、数据传输详细流程

2.1 数据传输方式

核心结论:数据传输采用 BE 到 BE 的直接快照复制机制,通过 HTTP 协议传输数据。

不是 JDBC:JDBC 仅用于元数据查询(SHOW PROC、SHOW DATA 等)
不是 StreamLoad:StreamLoad 用于外部数据导入,不是集群间复制

2.2 数据传输架构

┌─────────────────────────────────────────────────────────────┐
│ 迁移工具                                                     │
│ 生成 ReplicationJob(包含源 BE 信息)                       │
└──────────────────────┬──────────────────────────────────────┘
                       │ Thrift RPC
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 目标集群 FE                                                  │
│ 1. 接收 startTableReplication 请求                         │
│ 2. 验证 Token 和权限                                        │
│ 3. 创建事务(LoadJobSourceType = 'REPLICATION')            │
│ 4. 将任务分发给目标 BE 节点                                  │
└──────────────────────┬──────────────────────────────────────┘
                       │ 任务分发
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 目标集群 BE                                                  │
│ 1. 接收复制任务                                              │
│ 2. 解析源 BE 信息(host, httpPort)                         │
│ 3. 通过 HTTP 协议连接源 BE                                   │
│ 4. 请求快照数据(包含 srcTabletId, version, schemaHash)   │
│ 5. 接收快照数据并写入本地存储                                │
│ 6. 报告写入完成                                              │
└──────────────────────┬──────────────────────────────────────┘
                       │ HTTP 协议(快照下载)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 源集群 BE                                                    │
│ 1. 接收快照下载请求                                          │
│ 2. 验证 Token(从请求中获取)                                │
│ 3. 根据版本号生成快照                                        │
│ 4. 通过 HTTP 协议传输快照数据                                │
└─────────────────────────────────────────────────────────────┘

2.3 数据传输详细步骤

步骤 1:任务准备
// 在 produceReplicationJob() 中
ReplicationJob replicationJob = new ReplicationJob(
    jobId,                    // 任务 ID
    targetUserName,           // 目标集群用户名
    targetPassword,           // 目标集群密码
    sourceToken,              // 源集群 Token(关键!)
    targetDbId,               // 目标数据库 ID
    targetTableId,            // 目标表 ID
    dbName,                   // 数据库名
    tableName,                // 表名
    srcTableType,             // 源表类型
    srcTableDataSize,         // 源表数据大小
    partitionInfos            // 分区信息列表
);

// 分区信息包含:
PartitionInfo {
    partitionId,              // 目标分区 ID
    srcVersion,               // 源分区版本(关键!)
    indexInfos                // 索引信息列表
}

// 索引信息包含:
IndexInfo {
    indexId,                  // 目标索引 ID
    srcSchemaHash,            // 源 Schema Hash
    tabletInfos               // Tablet 信息列表
}

// Tablet 信息包含:
TabletInfo {
    tabletId,                 // 目标 Tablet ID
    srcTabletId,              // 源 Tablet ID(关键!)
    replicaInfos              // 副本信息列表
}

// 副本信息包含:
ReplicaInfo {
    srcBackend {              // 源 BE 节点信息(关键!)
        host,                 // 源 BE 主机地址
        bePort,               // BE 服务端口
        httpPort              // HTTP 端口(用于数据传输)
    }
}
步骤 2:RPC 调用
// 在 Utils.sendReplicationJob() 中
1. 随机选择目标集群 FE 节点
2.ReplicationJob 转换为 TTableReplicationRequest
3. 通过 Thrift RPC 调用:
   FrontendServiceProxy.call(
       address,               // 目标 FE 地址
       10000,                 // 超时时间(10秒)
       3,                     // 重试次数
       client -> client.startTableReplication(request)
   )
步骤 3:FE 处理请求
// 目标集群 FE 接收到请求后(FE 端逻辑,不在工具代码中)
1. 验证请求
   - 检查用户名和密码
   - 验证源集群 Token
   - 检查并发限制(replication_max_parallel_table_count 等)

2. 创建事务
   - 事务 Label: {dbId}-{tableId}_{jobId}
   - LoadJobSourceType: 'REPLICATION'
   - 事务状态: PREPARE

3. 分发任务到目标 BE 节点
   - 根据 Tablet 分布信息
   - 将任务发送到对应的 BE 节点
步骤 4:BE 节点拉取数据
// 目标 BE 节点接收到任务后(BE 端逻辑,不在工具代码中)
1. 解析任务信息
   - 获取源 BE 信息(host, httpPort)
   - 获取源 Tablet ID
   - 获取版本号和 Schema Hash

2. 建立 HTTP 连接
   - 连接到源 BE 的 httpPort(默认 8040- 使用源集群 Token 进行认证

3. 请求快照数据
   HTTP Request:
   - URL: http://{srcBeHost}:{httpPort}/api/snapshot/download
   - Method: POST
   - Headers:
     * Authorization: Token {sourceToken}
   - Body:
     {
       "tablet_id": srcTabletId,
       "version": srcVersion,
       "schema_hash": srcSchemaHash,
       "target_tablet_id": tabletId
     }

4. 源 BE 处理请求
   - 验证 Token
   - 根据版本号生成快照
   - 如果快照为空,返回错误:"Source snapshots is empty"
   - 否则,通过 HTTP 流式传输快照数据

5. 目标 BE 接收数据
   - 接收快照文件
   - 验证数据完整性(Checksum- 写入本地存储
   - 更新 Tablet 元数据
步骤 5:事务提交
// 所有副本写入完成后
1. 目标 BE 报告写入完成
2. FE 提交事务
   - 事务状态: COMMITTED
3. FE 发布数据
   - 事务状态: PUBLISHED
   - 数据变为可见(VISIBLE)
4. FE 完成事务
   - 事务状态: FINISHED
步骤 6:状态跟踪
// 迁移工具定期查询事务状态
1. 查询运行中的事务
   SHOW PROC '/transactions/{dbId}/running'
   - 如果找到对应的事务 → RUNNING

2. 查询已完成的事务
   SHOW PROC '/transactions/{dbId}/finished'
   - 如果找到且状态为 VISIBLE → FINISHED
   - 如果找到且状态为 ABORTED → FAILED

3. 更新任务状态
   - 更新 replicationJobState
   - 更新 replicationTableStatus
   - 统计进度

2.4 为什么采用这种方式?

2.4.1 为什么不是 JDBC?

JDBC 的特点

  • 基于 MySQL 协议
  • 用于 SQL 查询和 DDL 操作
  • 需要执行查询计划
  • 返回结果集,适合小数据量

为什么不适合

  1. 性能问题

    • 需要执行 SQL 查询,解析查询计划
    • 返回结果集需要序列化/反序列化
    • 网络往返次数多,效率低
  2. 功能限制

    • 无法直接获取 Tablet 级别的数据
    • 无法指定版本号获取特定版本的数据
    • 无法获取快照数据
  3. 资源消耗

    • 占用查询资源
    • 增加 FE 负载
    • 不适合大数据量传输

代码证据

// 工具中 JDBC 仅用于元数据查询
Utils.execQuerySql("SHOW PROC '/dbs/'", ...);
Utils.execQuerySql("SHOW DATA", ...);
Utils.execQuerySql("SHOW PARTITIONS", ...);
// 没有用于数据传输的 JDBC 调用
2.4.2 为什么不是 StreamLoad?

StreamLoad 的特点

  • 用于导入外部数据(文件、Kafka 等)
  • 客户端准备数据并上传
  • 通过 HTTP 接口上传数据流

为什么不适合

  1. 数据来源

    • StreamLoad 需要客户端准备数据
    • 迁移工具无法直接访问源集群的数据文件
    • 需要先导出再导入,效率低
  2. 数据格式

    • StreamLoad 需要数据格式转换
    • 需要解析和重新编码
    • 可能丢失元数据信息
  3. 一致性

    • StreamLoad 是导入操作,不是复制操作
    • 无法保证版本一致性
    • 无法支持增量同步

代码证据

// 请求中包含的是元数据信息,不是数据本身
TTableReplicationRequest {
    src_tablet_id,        // 源 Tablet ID
    src_version,          // 源版本号
    src_backend,          // 源 BE 信息
    // 没有数据内容
}
2.4.3 为什么采用 BE 到 BE 快照复制?

优势分析

  1. 性能优势

    ✅ 直接传输二进制数据,无需格式转换
    ✅ BE 到 BE 直连,减少网络跳数
    ✅ 支持并行传输,提高吞吐量
    ✅ 基于版本快照,支持增量同步
    
  2. 一致性保证

    ✅ 基于版本号,保证数据版本一致性
    ✅ 快照是原子操作,要么全部成功要么全部失败
    ✅ 支持事务机制,保证数据完整性
    
  3. 资源效率

    ✅ 不占用 FE 查询资源
    ✅ 不执行查询计划,减少 CPU 消耗
    ✅ 直接传输存储格式,减少内存消耗
    
  4. 灵活性

    ✅ 支持指定版本号复制特定版本的数据
    ✅ 支持增量同步(只传输版本差异)
    ✅ 支持断点续传(快照机制)
    
  5. 安全性

    ✅ 使用 Token 进行认证
    ✅ BE 到 BE 直连,减少中间环节
    ✅ 支持网络地址映射,适应复杂网络环境
    

代码证据

// 1. 请求中包含源 BE 信息
ReplicationJob.BackendInfo {
    host,      // 源 BE 主机地址
    bePort,    // BE 服务端口
    httpPort   // HTTP 端口(用于数据传输)
}

// 2. 请求中包含版本信息
PartitionInfo {
    srcVersion  // 源分区版本号
}

// 3. 错误信息显示使用快照机制
if (errorMsg.contains("Source snapshots is empty")) {
    // 说明使用快照机制
}

2.5 数据传输关键技术点

2.5.1 版本控制
// 版本比较逻辑
if (srcPartition.getVisibleVersion() > targetPartition.getVisibleVersion()) {
    // 需要同步:目标 BE 请求指定版本号的快照
    request.version = srcPartition.getVisibleVersion();
}

作用

  • 保证数据版本一致性
  • 支持增量同步
  • 避免重复传输
2.5.2 快照机制
// 快照请求
{
    "tablet_id": srcTabletId,      // 源 Tablet ID
    "version": srcVersion,          // 版本号
    "schema_hash": srcSchemaHash,   // Schema Hash
    "target_tablet_id": tabletId    // 目标 Tablet ID
}

特点

  • 快照对应特定版本的数据
  • 快照是只读的,保证数据一致性
  • 支持增量快照(只包含版本差异)
2.5.3 Token 认证
// 请求中包含源集群 Token
TTableReplicationRequest {
    src_token: "wwwwwwww-xxxx-yyyy-zzzz-uuuuuuuuuu"
}

// 目标 BE 使用 Token 访问源 BE
HTTP Request Headers:
    Authorization: Token {src_token}

作用

  • 验证目标集群是否有权限访问源集群
  • 防止未授权访问
  • 保证数据安全
2.5.4 并发控制
// FE 配置参数
replication_max_parallel_table_count    // 最大并发表数
replication_max_parallel_replica_count   // 最大并发副本数
replication_max_parallel_data_size_mb    // 最大并发数据量

作用

  • 控制并发度,避免过载
  • 平衡迁移速度和集群负载
  • 防止资源耗尽

2.6 数据传输流程图

┌─────────────────────────────────────────────────────────────┐
│ 迁移工具                                                     │
│ 1. 生成 ReplicationJob                                      │
│    - 包含源 BE 信息(host, httpPort)                       │
│    - 包含 Tablet 映射(srcTabletId → tabletId)             │
│    - 包含版本信息(srcVersion)                             │
│    - 包含 Token(srcToken)                                 │
└──────────────────────┬──────────────────────────────────────┘
                       │ Thrift RPC
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 目标集群 FE                                                  │
│ 1. 验证请求(Token、权限)                                  │
│ 2. 检查并发限制                                              │
│ 3. 创建事务(Label: {dbId}-{tableId}_{jobId})             │
│ 4. 分发任务到目标 BE 节点                                    │
└──────────────────────┬──────────────────────────────────────┘
                       │ 任务分发
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 目标集群 BE                                                  │
│ 1. 接收任务                                                  │
│ 2. 解析源 BE 信息                                            │
│ 3. 建立 HTTP 连接                                            │
│ 4. 发送快照下载请求                                          │
│    POST http://{srcBeHost}:{httpPort}/api/snapshot/download │
│    Headers: Authorization: Token {srcToken}                │
│    Body: {tablet_id, version, schema_hash, target_tablet_id}│
└──────────────────────┬──────────────────────────────────────┘
                       │ HTTP 协议
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 源集群 BE                                                    │
│ 1. 验证 Token                                                │
│ 2. 根据版本号生成快照                                        │
│ 3. 通过 HTTP 流式传输快照数据                               │
│ 4. 返回数据(或错误:"Source snapshots is empty")          │
└──────────────────────┬──────────────────────────────────────┘
                       │ HTTP Response(快照数据)
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 目标集群 BE                                                  │
│ 1. 接收快照数据                                              │
│ 2. 验证数据完整性(Checksum)                                │
│ 3. 写入本地存储                                              │
│ 4. 更新 Tablet 元数据                                        │
│ 5. 报告写入完成                                              │
└──────────────────────┬──────────────────────────────────────┘
                       │ 完成报告
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 目标集群 FE                                                  │
│ 1. 接收所有副本的完成报告                                    │
│ 2. 提交事务(COMMITTED)                                     │
│ 3. 发布数据(PUBLISHED → VISIBLE)                          │
│ 4. 完成事务(FINISHED)                                      │
└──────────────────────┬──────────────────────────────────────┘
                       │ 状态查询
                       ▼
┌─────────────────────────────────────────────────────────────┐
│ 迁移工具                                                     │
│ 1. 查询事务状态                                              │
│    SHOW PROC '/transactions/{dbId}/running'                  │
│    SHOW PROC '/transactions/{dbId}/finished'                 │
│ 2. 更新任务状态                                              │
│    - RUNNING / FINISHED / FAILED                            │
│ 3. 统计进度                                                  │
└─────────────────────────────────────────────────────────────┘

2.7 数据传输性能优化

2.7.1 并行传输
  • 表级并行:多个表同时传输
  • 分区级并行:多个分区同时传输
  • Tablet 级并行:多个 Tablet 同时传输
2.7.2 批量处理
  • 批量发送任务replicationJobBatchSize 控制
  • 批量查询状态:一次查询多个事务状态
2.7.3 网络优化
  • BE 直连:减少网络跳数
  • HTTP 协议:成熟稳定,支持大文件传输
  • 快照机制:支持增量传输,减少数据量

三、总结

3.1 迁移逻辑总结

  1. 多线程协调:4 个工作线程各司其职
  2. 周期性同步:定期更新元数据并生成任务
  3. 任务队列管理:DDL 任务和复制任务分别管理
  4. 状态跟踪:通过查询事务状态跟踪任务进度

3.2 数据传输总结

  1. 传输方式:BE 到 BE 直接传输,使用 HTTP 协议
  2. 数据格式:基于版本快照的二进制数据
  3. 认证机制:使用 Token 进行认证
  4. 优势:高效、可靠、灵活、安全

3.3 关键设计决策

决策原因
BE 到 BE 直连减少网络跳数,提高性能
HTTP 协议成熟稳定,支持大文件传输
快照机制保证数据一致性,支持增量同步
Token 认证保证安全性,简化认证流程
版本控制支持增量同步,避免重复传输

文档生成时间:2025-01-XX
反编译工具:CFR 0.152
JAR 版本:starrocks-cluster-sync-2.0

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roman_日积跬步-终至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值