Apache Doris 实战教程:手把手实现 ClickHouse 表结构迁移与数据校验

发布时间:2026/11/5 20:52:18

Apache Doris 实战教程:手把手实现 ClickHouse 表结构迁移与数据校验 阅读前提本教程假设你已具备基本的 ClickHouse 使用经验了解 MergeTree、分布式表概念已部署 Apache Doris 集群单节点或集群均可会使用基本的 SQL 和 Shell 命令第一步环境准备 连接 Doris首先确认 Doris 集群可连接并创建目标 Database-- 连接 Doris默认 MySQL 协议端口 9030 -- mysql -h 127.0.0.1 -P 9030 -u root -- 创建目标数据库 CREATE DATABASE IF NOT EXISTS migration_demo; -- 查看集群状态 SHOW BACKENDS; SHOW FRONTENDS;第二步DDL 自动转换——从 CK 到 Doris2.1 核心映射规则ClickHouse 和 Apache Doris 在数据类型、表引擎、分区策略上存在差异需要做以下映射# ck_to_doris_ddl.py —— DDL 类型映射核心代码 # ClickHouse → Doris 类型的映射表覆盖快手支持的 22 种 TYPE_MAP { UInt8: TINYINT, UInt16: SMALLINT, UInt32: INT, UInt64: BIGINT, Int8: TINYINT, Int16: SMALLINT, Int32: INT, Int64: BIGINT, Float32: FLOAT, Float64: DOUBLE, String: VARCHAR(65533), FixedString: CHAR, Date: DATE, DateTime: DATETIME, DateTime64: DATETIME, Array: ARRAY, Map: MAP, LowCardinality: VARCHAR, # LowCardinality 展开为 VARCHAR Enum8: TINYINT, # Enum 通常转为基础类型 Enum16: SMALLINT, } def convert_type(ck_type): 转换 ClickHouse 类型到 Doris 类型 # 处理 Nullable(Type) if ck_type.startswith(Nullable(): inner ck_type[9:-1] return convert_type(inner) # Doris 默认支持 NULL去除 Nullable 包装 # 处理 Array(Type) if ck_type.startswith(Array(): inner ck_type[6:-1] return fARRAY{convert_type(inner)} # 基本类型映射 return TYPE_MAP.get(ck_type, VARCHAR(65533)) # 未知类型兜底为 VARCHAR2.2 生成完整的 Doris DDL# 接上文件 ck_to_doris_ddl.py import re def generate_doris_ddl(ck_table_name, ck_columns, ck_partition_by, ck_order_by): 根据 ClickHouse 表信息生成 Doris DDL 参数: ck_table_name: ClickHouse 表名 ck_columns: [(col_name, col_type), ...] ck_partition_by: ClickHouse 分区表达式 ck_order_by: ClickHouse 排序键列名列表 doris_table ck_table_name.replace(., _) # 1. 生成列定义 col_defs [] for col_name, col_type in ck_columns: doris_type convert_type(col_type) col_defs.append(f {col_name} {doris_type}) # 2. 从 ORDER BY 推导 DUPLICATE KEY前两列作为 Key key_cols ck_order_by[:2] if len(ck_order_by) 2 else ck_order_by # 3. 分区策略取第一个 Date/DateTime 列做 RANGE 分区 partition_col ck_partition_by if ck_partition_by else event_date # 4. 分桶按第一列 HASH默认 32 桶 bucket_col ck_order_by[0] if ck_order_by else ck_columns[0][0] bucket_num 32 # 可根据分区数据量调整 ddl f CREATE TABLE IF NOT EXISTS {doris_table} ( {,.join(col_defs)} ) ENGINE OLAP DUPLICATE KEY({,.join(key_cols)}) PARTITION BY RANGE({partition_col}) () DISTRIBUTED BY HASH({bucket_col}) BUCKETS {bucket_num} PROPERTIES ( replication_num 3 ); return ddl.strip() # ---------- 使用示例 ---------- if __name__ __main__: # 模拟一个 ClickHouse 表结构 ck_columns [ (event_date, Date), (user_id, UInt64), (event_type, String), (event_value, Float64), (event_time, DateTime), (tags, Array(String)), (ext_info, Map(String, String)), ] ck_order_by [user_id, event_time] ddl generate_doris_ddl( ck_table_namedb.ck_user_behavior, ck_columnsck_columns, ck_partition_byevent_date, ck_order_byck_order_by ) print(-- 生成的 Doris DDL --) print(ddl)执行结果将生成类似以下 DDL-- 生成的 Doris DDL -- CREATE TABLE IF NOT EXISTS db_ck_user_behavior ( event_date DATE, user_id BIGINT, event_type VARCHAR(65533), event_value DOUBLE, event_time DATETIME, tags ARRAYVARCHAR(65533), ext_info MAPVARCHAR(65533),VARCHAR(65533) ) ENGINE OLAP DUPLICATE KEY(user_id, event_time) PARTITION BY RANGE(event_date) () DISTRIBUTED BY HASH(user_id) BUCKETS 32 PROPERTIES ( replication_num 3 );第三步配置双跑——数据同时写入 CK 和 Doris迁移期间新增数据需要同时写入 ClickHouse 和 Apache Doris#!/bin/bash # dual_write.sh —— 离线数据双写脚本 DORIS_HOST127.0.0.1 DORIS_PORT8030 # Stream Load HTTP 端口 DORIS_USERroot DORIS_DBmigration_demo DORIS_TABLEuser_behavior # 假设已有 Hive → CK 的导出文件 data_export.csv DATA_FILE/data/export/data_export.csv # Stream Load 写入 Doris curl --location-trusted -u ${DORIS_USER}: \ -H label:dual_write_$(date %s) \ -H column_separator:, \ -H format:csv \ -T ${DATA_FILE} \ http://${DORIS_HOST}:${DORIS_PORT}/api/${DORIS_DB}/${DORIS_TABLE}/_stream_load echo Dual write completed: $(date)第四步存量数据迁移——Hive 到 Doris对于上游 Hive 数据完整的情况直接复用已有 ETL 链路-- 通过 Doris Multi-Catalog 直接查询 Hive 并 INSERT INTO 目标表 -- 适用于 Hive 数据保留周期够长的场景 -- 1. 创建 Hive Catalog CREATE CATALOG hive_catalog PROPERTIES ( type hms, hive.metastore.uris thrift://hive-metastore:9083 ); -- 2. 将 Hive 数据批量写入 Doris 内表 INSERT INTO migration_demo.user_behavior SELECT event_date, user_id, event_type, event_value, event_time FROM hive_catalog.ods.user_behavior_hive WHERE event_date 2025-01-01; -- 3. 查看导入状态 SHOW LOAD FROM migration_demo;第五步数据校验——确保迁移结果可靠5.1 基础数据量校验-- 第一层校验行数和基础统计 -- 在 Doris 中执行 SELECT Doris AS source, COUNT(*) AS row_count, SUM(event_value) AS total_value, COUNT(DISTINCT user_id) AS unique_users FROM migration_demo.user_behavior UNION ALL -- 对比 ClickHouse需单独查询后贴入 -- 预期结果row_count 和 total_value 应一致 SELECT ClickHouse AS source, 1000000, 12345678.90, 50000;5.2 维度聚合校验-- 第二层按核心维度 GROUP BY 对比 SELECT event_date, event_type, COUNT(*) AS cnt, SUM(event_value) AS sum_val, AVG(event_value) AS avg_val FROM migration_demo.user_behavior WHERE event_date 2025-06-01 GROUP BY event_date, event_type ORDER BY event_date, event_type LIMIT 100; -- 将此结果与 ClickHouse 中相同查询结果逐行对比5.3 Float 精度校验# float_check.py —— 精度容忍校验 import math def check_float_precision(ck_value, doris_value, tolerance0.001): 检查 Float 类型精度偏差 参数: ck_value: ClickHouse 侧数值 doris_value: Doris 侧数值 tolerance: 相对容忍阈值默认 0.1% if ck_value 0 and doris_value 0: return True relative_error abs(ck_value - doris_value) / max(abs(ck_value), abs(doris_value)) if relative_error tolerance: print(f⚠ 精度超标: CK{ck_value}, Doris{doris_value}, error{relative_error:.6f}) return False return True # 使用示例 assert check_float_precision(3.14159265, 3.14159) # True偏差约 0.00008% assert not check_float_precision(3.14, 3.25) # False偏差约 3.5%超标第六步灰度切换#!/bin/bash # gray_switch.sh —— 通过 OneSQL 或其他路由层逐步切换 # 1. 10% 流量打向 Doris echo Gray release: 10% traffic # onesql-cli set-route --table user_behavior --target doris --weight 10 # 2. 观察 24 小时确认无异常 # 3. 50% 流量 echo Gray release: 50% traffic # onesql-cli set-route --table user_behavior --target doris --weight 50 # 4. 观察 24 小时 # 5. 100% 流量 echo Full switch to Doris # onesql-cli set-route --table user_behavior --target doris --weight 100常见问题QDDL 转换后查询变慢了A检查三点① Bucket 数是否根据数据量合理设置太小导致并发不足太大增加元数据压力② 分桶 Key 是否与查询模式匹配③ 需要 Join 的两张表是否配置了 Colocation Group。QStream Load 导入报错 too many open filesADoris BE 进程需要调整ulimit -n建议设为 65536 以上。Q补数期间 Doris 查询性能受影响A建议将补数任务放在业务低峰期执行或在导入时设置strict_mode false降低导入对查询的影响。
延伸阅读

更多相关文章

2026/11/5 17:01:17

AI应用开发四层技术栈:MCP协议与模块化实践

1. AI应用开发四层技术栈全景解读当我在2023年首次接触金融大模型问答机器人项目时,面对琳琅满目的技术方案曾一度陷入选择困难。直到梳理出MCP-Skills-Tool Calling-SubAgent这套分层架构,才真正打通了AI应用开发的任督二脉。这套架构就像建造摩天大楼的…

2026/11/5 16:30:09

【00002】

二、C语言2.1基本数据类型1.C语言开发步骤(1) vi编辑器1.使用vi打开软件vi filename.cvi编辑器三种模式: 底行模式(默认,其余模式可以ESC进入底行模式) 插入模式(底行模式输入i、a、o均可进入插…

2026/10/31 1:11:39

1A,30VIN,双灯,XZ4056V,反接保护

产品概述这是一款完整的单节锂离子电池采用涓流、恒流、恒压控制的线性充电器,充电电流范围100mA-1000mA之间可设置。芯片底部有散热片的ESOP8封装与较少的外部元件数目而成为便携式应用的理想选择。可以适合USB电源和适配器电源工作。该充电器有电池温度监控&#…

2026/11/5 16:51:33

EasyU优启通PE维护盘实战指南:制作、启动与核心操作全解析

1. 为什么一个PE维护盘值得单独拿出来聊装系统、修引导、救数据、清病毒,这些活儿但凡干过几次的人都知道,真正决定效率的往往不是你手速多快,而是手边那套PE环境靠不靠谱。EasyU(圈内习惯叫优启通)就是在这个场景下被…

2026/11/5 16:26:13

Rust原来很简单二:所有权系统

Part 2 核心篇 —— 所有权系统 本篇覆盖 第 6 ~ 10 章:所有权、借用与引用、生命周期、字符串与切片、内存模型。 ⚠️ 这是全书最重要的一篇。 把它吃透,Rust 从此“很简单”;跳过它,你会处处碰壁。请慢下来,动手敲每一段代码。 第 6 章 所有权:Rust 的第一性原理 6…

2026/11/5 15:00:49

基于Hadoop+Spark的民宿智能推荐系统设计与实现

1. 项目概述:大数据时代的民宿智能推荐这个毕业设计项目融合了当前大数据领域最主流的技术栈,构建了一个完整的民宿推荐系统解决方案。作为一名长期从事大数据系统开发的工程师,我认为这个选题非常贴合实际应用场景——随着旅游业的复苏和共享…

2026/11/5 14:10:30

ADS122U04IPWR+STM32F427ZI工业级温度测量信号链设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配RS485,解决远距离温度监控难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 14:10:30

UART接口24位ADC搭配PIC18F46K22的RTD与热电偶测温实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/5 0:02:07

多智能体协作系统架构设计与实践优化

1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中…

2026/11/5 0:02:07

大模型系统性入门实战指南:从原理到部署微调的全链路路线

提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#…

2026/11/5 0:02:07

鸿蒙文件系统沙箱机制解析与开发实践

1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的…

2026/11/5 15:39:06

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/4 21:04:25

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑