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降低导入对查询的影响。

相关新闻

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

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

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

2026/7/24 23:15:07阅读更多 →
6款免费AI工具助力高效毕业论文写作

6款免费AI工具助力高效毕业论文写作

1. 毕业论文写作的现状与挑战又到了一年一度的毕业季,对于即将在2026年毕业的大学生来说,论文写作无疑是最大的挑战之一。根据我多年指导论文的经验,90%的学生都会陷入"选题难、写作慢、格式乱"的困境。传统的论文写作流程通常包括…

2026/7/24 23:15:07阅读更多 →
解锁专业级直播特效:StreamFX 12大核心功能深度探索

解锁专业级直播特效:StreamFX 12大核心功能深度探索

解锁专业级直播特效:StreamFX 12大核心功能深度探索 【免费下载链接】obs-StreamFX StreamFX is a plugin for OBS Studio which adds many new effects, filters, sources, transitions and encoders! Be it 3D Transform, Blur, complex Masking, or even custom …

2026/7/24 23:13:07阅读更多 →
终极指南:3步轻松解锁网易云音乐NCM加密文件,实现音乐自由播放

终极指南:3步轻松解锁网易云音乐NCM加密文件,实现音乐自由播放

终极指南:3步轻松解锁网易云音乐NCM加密文件,实现音乐自由播放 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 还在为网易云音乐下载的…

2026/7/25 0:41:22阅读更多 →
猫抓视频嗅探工具:你的网页视频下载专家指南

猫抓视频嗅探工具:你的网页视频下载专家指南

猫抓视频嗅探工具:你的网页视频下载专家指南 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 在数字内容无处不在的今天,我们…

2026/7/25 0:41:22阅读更多 →
商家降本、骑手增收,诚心呈意为何是配送行业的破局答案?

商家降本、骑手增收,诚心呈意为何是配送行业的破局答案?

跑同城配送的骑手,大多有过这样的感受:路上同行的人越来越多,手里的配送单价却一降再降,想多赚点就得熬更长的时间、跑更远的路;做餐饮的商家也有苦难言:线上订单看着红火,扣完平台抽成和配送费…

2026/7/25 0:41:22阅读更多 →
Web 安全怎么学,OWASP Top 10 漏洞原理与 Burp Suite 实战详解

Web 安全怎么学,OWASP Top 10 漏洞原理与 Burp Suite 实战详解

从原理到实战:Web 核心漏洞深度剖析 很多初学者在掌握了 Linux 基础和网络协议后,面对 Web 渗透测试往往感到无从下手。要么过度依赖自动化工具“一键扫描”,要么死记硬背 Payload 却不懂其背后的运行逻辑。真正的进阶之路,在于深…

2026/7/25 0:41:22阅读更多 →
C++的并发与内存模型:从原子操作到内存屏障

C++的并发与内存模型:从原子操作到内存屏障

C11引入了标准化的内存模型和多线程支持。这不是简单地在语言层面加了几个库——它重新定义了C程序在并发环境下的行为基础。理解这个内存模型,是写出正确并发程序的前提。一、内存模型:为什么需要它多线程环境下,代码的执行顺序不是源代码顺…

2026/7/25 0:41:22阅读更多 →
D3KeyHelper暗黑3按键助手:免费开源的游戏自动化终极指南

D3KeyHelper暗黑3按键助手:免费开源的游戏自动化终极指南

D3KeyHelper暗黑3按键助手:免费开源的游戏自动化终极指南 【免费下载链接】D3keyHelper D3KeyHelper是一个有图形界面,可自定义配置的暗黑3鼠标宏工具。 项目地址: https://gitcode.com/gh_mirrors/d3/D3keyHelper 还在为暗黑破坏神3中繁琐的重复…

2026/7/25 0:39:22阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 0:58:53阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 0:58:53阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 0:58:53阅读更多 →
突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:01:16阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:01:16阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:01:16阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/24 23:01:03阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/24 19:00:40阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/24 19:00:40阅读更多 →