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

发布时间:2026/9/11 19:52:50

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/9/12 8:40:33

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

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

2026/9/12 1:33:29

【00002】

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

2026/9/5 1:26:51

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

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

2026/9/12 8:40:12

AI Agent全栈开发指南:从基础原理到生产级项目实战

1. 先弄清楚 AI Agent 到底在解决什么问题去年这个时候,还有人在群里问 AI Agent 是不是又一个概念泡沫。到了 2026 年,这个问题基本没人问了——招聘平台上挂着「agent 开发」字样的岗位翻了不止一倍,面试里开始出现「你怎么设计一个多智能体…

2026/9/12 8:40:12

AI工程化落地:用OpenSpec与OPSX构建规范驱动的开发工作流

开发 AI 应用两年多,我最大的感触不是模型不够聪明,而是工程化太松散。单看一次代码生成,AI 确实惊艳,但一旦进入多轮修改、多人协作、跨会话交接,就会出现“前面说好的需求,后面全忘了”的情况。后来接触到…

2026/9/12 8:40:12

山林边缘火灾预警系统:YOLOv8/v11实战部署与多模型协同设计

1. 这不是个“玩具项目”,而是一套能真正在山林边缘跑起来的火灾预警系统我去年在云南普洱一个国有林场驻点三个月,跟着护林员巡山时亲眼见过两次小规模火情——一次是雷击引燃枯枝,另一次是游客丢弃未熄灭的烟头。火苗蹿起来不到两分钟&…

2026/9/12 8:40:11

AI Agent记忆系统设计:四层架构与工程落地实践

1. 项目概述:为什么“让 Agent 记住你”不是功能升级,而是范式切换你有没有试过和某个 AI 助手聊了二十分钟,从查天气、订咖啡、改简历,再到讨论下周会议的 PPT 结构,它全程都记得你刚说“我讨厌蓝色系配色”&#xff…

2026/9/12 2:05:33

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/12 3:55:12

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/12 0:04:17

MATLAB仿生优化框架:长鼻浣熊算法多策略融合实现

简介:本资源是一份面向智能优化算法研究者与MATLAB初学者的仿生智能算法实践代码包,聚焦于长鼻浣熊优化算法(COA)的多策略改进与性能验证。针对传统COA易陷局部最优、收敛精度不足等问题,作者融合Circle映射初始化提升…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 JavaWeb 的校园一卡通管理系统的设计与实现 基于 JavaWeb 的校园卡业务管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/9/12 0:04:17

【JAVA毕设源码分享】基于 Java 的图书馆借阅管理平台的搭建与实现 基于 Java 的图书馆综合管理系统(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/12 6:37:43

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

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

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

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