我把实时报表从 ClickHouse 迁到 Doris 后,导入吞吐从 8k/s 升到 12 万/s
我把实时报表从 ClickHouse 迁到 Doris 后导入吞吐从 8k/s 升到 12 万/s上周业务方在群里甩过来一张 SQL 截图我一看就皱眉头了。“这个实时看板怎么又卡了15 秒还没出结果。”我们做的是电商运营的实时大盘——GMV、订单热区、用户漏斗每 5 秒刷一次。一开始用的是 ClickHouse扛了好几个月。结果最近几天双 11 预热订单流水从平时的 1k/s 涨到 8k/s导入就开始堵车了。最离谱的一次看板卡了 47 秒才出数。运营总监直接打电话过来——你们这个数据是不是坏了我心里很清楚不是 ClickHouse 不行是场景选错了。ClickHouse 强在 OLAP 大宽表的复杂聚合不在高频小批量实时写入。我们把它当实时数仓用每天导入 7000 万条记录结果高频写入时各种 part 合并卡住后台查询也跟着抖。折腾了两周我把链路从 ClickHouse 迁到 Apache Doris。导入吞吐从 8k/s 涨到12 万/sP99 查询延迟从 1.4s 压到 180ms业务看板彻底没再卡过。今天把这次迁移讲清楚——不是 ClickHouse 不好是不同引擎有它适合的战场。背景为什么 ClickHouse 在我们这里水土不服先说清楚我们原本的架构。业务 MySQL主从 → Canal → Kafka → ClickHouseReplacingMergeTree ↑ Grafana 实时看板看似很标准问题出在三个地方。问题一高频小批量写入触发 part 风暴。ClickHouse 的存储模型是每次写入产生一个 part后台异步 merge。小批量1k 条/批高频每秒一批写入时part 数量会快速膨胀。我们高峰期看到后台有 800 个 part 在排队 mergemerge 占满了磁盘 IO。system.parts里直接报警SELECTcount()FROMsystem.partsWHEREtableorder_log;-- 返回 847意味着有 847 个未合并的 part问题二ReplacingMergeTree 的最终一致性对实时看板不友好。我们用 ReplacingMergeTree 做订单去重同一订单号可能因补偿逻辑写多次。问题是ReplacingMergeTree 的去重只在 part merge 时发生。part 堆积时同一订单可能出现多次看板上的实时订单数会跳变——前一秒 1.2 万下一秒 1.5 万业务方完全没法用。问题三多表 JOIN 在 ClickHouse 上是大宽表场景。我们订单表要 JOIN 用户表、商品表、营销活动表4-5 张表 JOIN 在 ClickHouse 上要靠大宽表预聚合。每次加新维度比如新增营销活动类型都要重建大宽表ETL 改造成本非常高。为什么选 Doris 不是 Impala / StarRocks迁之前我们做了一圈调研对比了三个候选引擎实时写入复杂 JOINMySQL 兼容运维成本ClickHouse中part merge 痛点弱大宽表无中Doris强Stream Load 主键模型强自适应 JOIN强MySQL 协议低StarRocks强强中中选 Doris 不是因为它最强是因为Doris 的 MySQL 协议兼容 Stream Load Unique Key 模型刚好对上了我们的痛点。选 Doris 的三个核心理由MySQL 协议兼容——业务方继续用 MySQL 客户端连DBA 不用学新东西Stream Load HTTP 接口——Kafka 消费端可以直接 HTTP 推送比 ClickHouse 的 Kafka 表灵活Unique Key 模型 强一致的实时去重——没有 ReplacingMergeTree 的最终一致问题下面是迁移后的链路业务 MySQL → Canal → Kafka → Flink (轻量 ETL) → Doris Stream Load ↑ Grafana 实时看板Flink 只做一层轻量 ETL字段映射、过滤异常值不做聚合。所有实时聚合都在 Doris 里做。第一步Doris 集群部署与表模型选型Doris 集群我们用 3 FE 6 BE 的结构中等规模生产环境够用# fe.conf 核心配置http_port 8030 rpc_port 9020 query_port 9030 priority_networks 192.168.0.0/16# be.conf 核心配置be_port 9060 webserver_port 8040 storage_root_path /data1/doris;/data2/doris# 多盘 IO 分散表模型选择——Doris 有 Aggregate、Unique、Duplicate 三种模型我们订单表用Unique KeyCREATETABLEorder_log(order_idBIGINTNOTNULL,user_idBIGINTNOTNULL,sku_idBIGINTNOTNULL,amountDECIMAL(18,2)NOTNULL,statusVARCHAR(32)NOTNULL,created_atDATETIMENOTNULL,updated_atDATETIMENOTNULL)UNIQUEKEY(order_id,created_at)PARTITIONBYRANGE(created_at)()DISTRIBUTEDBYHASH(order_id)BUCKETS32PROPERTIES(replication_num3,enable_unique_key_merge_on_writetrue-- 关键开启 merge-on-write);关键点enable_unique_key_merge_on_write true。这个属性是 Doris 1.2 之后的杀手锏。默认 Unique Key 是读时合并类似 ClickHouse 的 ReplacingMergeTree开启 merge-on-write 后变成写时合并——同一 order_id 的多次写入在 BE 层直接覆盖没有 part 堆积问题也没有最终一致跳变。我做了个对比测试模型8k/s 持续写入看板数据跳变P99 查询ClickHouse ReplacingMergeTree5 分钟后 part 堆积频繁跳变1.4sDoris Unique Key默认流畅偶尔跳变220msDoris Unique Keymerge-on-write流畅不跳变180ms差距最明显的是看板跳变——从 0 次/分钟到接近 0。这是业务方最关心的指标。第二步Stream Load 替代 Kafka 引擎表ClickHouse 的 Kafka 表让我们又爱又恨——配置简单但太脆。-- ClickHouse 旧配置已废弃CREATETABLEorder_log_kafka(order_id UInt64,user_id UInt64,...)ENGINEKafka()SETTINGS kafka_broker_listkafka:9092,kafka_topic_listorder_log,kafka_group_nameclickhouse_consumer,kafka_formatJSONEachRow;这套配置的问题ClickHouse 直接当 Kafka 消费者没办法做字段转换、过滤异常、关联维表。脏数据进来直接污染整张表。Doris 的 Stream Load 思路完全不同——Doris 只管怎么高效接收数据不管数据从哪来# Stream Load HTTP 接口最基础的用法curl--location-trusted-uroot:\-Hlabel: order_log_${date}_${uuid}\-Hcolumn_separator:,\-T/tmp/order_log.csv\http://doris-fe:8030/api/order_db/order_log/_stream_loadlabel 参数是关键——Doris 通过 label 保证 Exactly-Once 语义同一 label 多次提交只生效一次。配合 Flink 的两阶段提交能做到端到端不丢不重。我们用 Flink 做中间层Flink 消费 Kafka 后调用 Stream Load// Flink Doris Sink (简化版)publicclassDorisStreamLoadSinkextendsRichSinkFunctionString{privateHttpClienthttpClient;Overridepublicvoidinvoke(Stringvalue,Contextcontext)throwsException{// value 是一行 CSV 格式HttpPostpostnewHttpPost(http://doris-fe:8030/api/order_db/order_log/_stream_load);post.setHeader(label,flink_UUID.randomUUID().toString());post.setHeader(column_separator,,);post.setEntity(newStringEntity(value,ContentType.TEXT_PLAIN));// Basic Authpost.setHeader(Authorization,Basic Base64.encode(root:));httpClient.execute(post);}}这一步我们做了几个优化优化 1攒批写入。Flink 默认每条数据触发一次 invoke8k/s 订单就意味着 8000 次/秒 HTTP 请求Doris FE 扛不住。我们改成每 5000 条或 1 秒攒一批.buffer(5000,TimeUnit.SECONDS.toMillis(1))优化 2并行多 Sink。单 Sink 写入上限就是单 FE 的处理能力。我们起了 8 个并行 Sink 写同一张表的 32 个 bucket不同 bucket 间互不冲突// Flink envenv.addSink(newDorisStreamLoadSink()).setParallelism(8).name(doris-sink);优化 3失败重试 label 去重。如果 Stream Load 因为网络抖动失败Flink 重试时用同一个 label重发Doris 会自动去重label 库保留 3 天。这样即使 Flink 重启也不会重复写入。第三步复杂查询从 ClickHouse 迁过来的改造业务方有 200 多条 SQL 要迁最大头是这个 5 表 JOIN 的实时看板-- 原 ClickHouse SQL4-5 张表 JOINSELECTdate_trunc(minute,o.created_at)ASminute,p.category_name,COUNT(DISTINCTo.order_id)ASorder_cnt,SUM(o.amount)ASgmvFROMorder_log oJOINuser_dim uONo.user_idu.user_idJOINsku_dim sONo.sku_ids.sku_idJOINpromotion_dim pONo.promo_idp.promo_idJOINstore_dim stONo.store_idst.store_idWHEREo.created_atNOW()-INTERVAL1HOURGROUPBYminute,p.category_nameClickHouse 上跑要 1.4 秒已经建了大宽表迁到 Doris 后我没建大宽表——因为 Doris 的自适应 JOIN 优化器能直接处理多表 JOIN-- Doris 上同样的 SQL零改造SELECTdate_trunc(o.created_at,minute)ASminute,p.category_name,COUNT(DISTINCTo.order_id)ASorder_cnt,SUM(o.amount)ASgmvFROMorder_log oJOINuser_dim uONo.user_idu.user_idJOINsku_dim sONo.sku_ids.sku_idJOINpromotion_dim pONo.promo_idp.promo_idJOINstore_dim stONo.store_idst.store_idWHEREo.created_atNOW()-INTERVAL1HOURGROUPBYminute,p.category_name;第一次跑620ms。比 ClickHouse 还快。我又用EXPLAIN看执行计划Doris 的 CBO 优化器自动把 4 张小维表user_dim、sku_dim、promotion_dim、store_dim做了Broadcast Join——把维表全量广播到 BE 节点订单大表在本地做 Hash Join省掉了 Shuffle Join 的网络开销。对比 ClickHouse 的 JOIN 改造维度ClickHouse 方案Doris 方案是否需要大宽表是否新增维度改造重建宽表全量加 JOIN增量5 表 JOIN 耗时1.4s620ms内存占用大宽表 8GB维表各 200MB业务方第一次看到 620ms 的查询结果时运营总监还以为我没跑完。之前 ClickHouse 要 1.4 秒结果回到界面要等 1.5-2 秒肉眼能感觉到卡顿。Doris 这边几乎是刷一下就出。第四步compaction 调优与冷热数据分层Doris 也有 compaction 问题但比 ClickHouse 好调得多。compaction 调优的核心是compaction_policy和cumulative_size_based_promotion_ratio-- 表级别 compaction 配置ALTERTABLEorder_logSET(compaction_policytime_series,-- 时序数据用 time_series 策略time_series_compaction_goal_size_mbytes1024,-- 单 segment 目标大小time_series_compaction_file_count_threshold2000);time_series 策略专为高频写入 时序查询场景——它会主动把老数据合并成大 segment新写入保持小 segment 持续追加。避免 ClickHouse 那种part 数量爆炸的问题。冷热数据分层我们也做了-- 订单表按时间分区ALTERTABLEorder_logMODIFYPARTITIONBYRANGE(created_at)(PARTITIONp202606VALUESLESS THAN(2026-07-01),PARTITIONp202607VALUESLESS THAN(2026-08-01),PARTITIONp202608VALUESLESS THAN(2026-09-01));-- 历史分区放到冷盘HDDALTERTABLEorder_logMODIFYPARTITIONp202605SET(storage_mediumHDD,storage_cooldown_time2026-06-01 00:00:00);热数据最近 7 天放 SSD冷数据7 天自动滚动到 HDD。Doris 通过storage_cooldown_time自动迁移不用手动调度。踩坑记录迁移过程不是一帆风顺几个值得记下来的坑。1. Stream Load 的 label 重复导致数据丢失。我们一开始用时间戳生成 labellabel_${timestamp}结果同一秒内多个并发 Flink Sink 任务可能生成重复 label。Doris 对重复 label 的处理是返回成功但跳过写入——业务上等同于丢数。解决label 改成flink_${uuid}_${taskId}_${checkpointId}保证全局唯一。2. 第一次跑 SELECT COUNT(*) 直接超时。ClickHouse 上 count() 是毫秒级Doris 上居然超时。原因是我们的order_log表有 4 亿行**count() 触发全表扫描**。ClickHouse 之所以快是因为它有count()的特殊优化直接读元数据。Doris 没有这个优化。解决Doris 上别用 count(*)改用SHOW PARTITIONS或ADMIN SHOW REPLICA STATUS看行数或者用EXPLAIN看扫描量。如果真要精确 count加 WHERE 条件限定分区。3. JDBC 连接 Doris 报too many connections。业务方有 50 多个应用通过 MySQL 协议连 DorisDoris 的 FE 默认最多接受 1024 个连接这数字看起来很多但很多 BI 工具的连接池默认 50。解决FE 配置调大qe_max_connection 5000同时在 BI 工具侧降低连接池一般 10-20 足够。4. Unique Key 模型的删除性能问题。Doris 的 Unique Key 模型删除走的是标记删除 后台 compaction——单次 DELETE 影响 100 万行会卡 compaction 10 分钟。我们的解决方案用DELETE FROM table WHERE order_id IN (...)配合分区裁剪比如只删最近 3 天的脏数据DELETEFROMorder_logWHEREorder_idIN(123,456,789)ANDcreated_at2026-07-20;绝不要无分区条件的大批量 DELETE。5. Flink 攒批太大反而延迟。我们一开始设buffer(10000, 5s)——5 秒或 1 万条才写一次。结果 5 秒攒批让看板延迟从 1 秒变成 6 秒业务方又来找我了。调成buffer(5000, 1s)后导入吞吐和延迟达到最佳平衡点——12 万/s 峰值导入看板延迟稳定在 2 秒内。效果对比迁移前后关键指标对比指标ClickHouse 旧链路Doris 新链路变化峰值导入吞吐8k/s开始卡顿12 万/s15x实时看板 P99 查询1.4s180ms-87%5 表 JOIN 改造需大宽表零改造—看板数据跳变频繁几乎为零—FE/BE 资源占用3 节点 ClickHouse3 FE 6 BE持平存储成本3 个月热数据1.2TB SSD0.8TB SSD 1.5TB HDD-25%最让我们意外的是看板跳变问题。之前业务方每天都反馈数据不准运营复盘时经常因为跳变数据误判趋势。Doris 的 merge-on-write 之后运营再也没反馈过这个问题。写在最后ClickHouse 和 Doris 不是谁取代谁的关系——它们适合的场景不一样。ClickHouse 更适合日志分析、用户行为分析大宽表 复杂聚合查询写入频率不高分钟级或更低数据量特别大PB 级Doris 更适合实时数仓高频小批量写入多表 JOIN 频繁的实时报表需要 MySQL 协议兼容业务方学习成本低强一致的实时去重这次迁移的教训没有银弹引擎——选型要按场景来别被ClickHouse 性能好这种话术骗了Doris 的 merge-on-write 是杀手锏——别用默认的 Unique Key 模型一定要打开Stream Load 的 label 一定要全局唯一——这是 Exactly-Once 的基础compaction 调优按表类型来——时序数据用 time_series频繁更新用 size_based如果你也在做实时数仓选型强烈建议把 Doris 加进候选清单。它的 MySQL 兼容 Stream Load merge-on-write 这三个特性组合起来是实时数仓场景下非常完整的方案。—— 把 8k/s 干到 12 万/s 之后终于能睡个好觉的数仓工程师

相关新闻

最近iOS上架遇到甲方搞赛马机制(离了个大谱)

最近iOS上架遇到甲方搞赛马机制(离了个大谱)

iOS上架遇甲方赛马机制:外包开发者噩梦、App Store高危雷区全解析 一、什么是iOS场景下甲方的赛马上架模式? 常规赛马是企业内部多团队并行研发同类产品,以数据结果优胜劣汰、最终只保留一套主力产品。而放到iOS App外包交付、App Store上架…

2026/10/17 11:20:47 阅读更多 →
Amphenol ICC MSPEC2L0AL010线束替代方案解析

Amphenol ICC MSPEC2L0AL010线束替代方案解析

随着服务器、通信设备、工业控制以及智能制造系统不断向高集成化方向发展,设备内部连接方案的重要性越来越突出。线束组件不仅承担电力传输和信号连接任务,同时也影响设备整体稳定性、维护效率以及系统扩展能力。 Amphenol ICC (Commercial Products) 是…

2026/10/16 21:42:34 阅读更多 →
BSCI A 级也没用?Shein 的 SRS 三合一审核,把广州十三行的厂老板问哑了

BSCI A 级也没用?Shein 的 SRS 三合一审核,把广州十三行的厂老板问哑了

01 番禺做女装的林姐,上个月试单 3 万件黄了林姐厂里 120 人,做针织女装八年,给十三行档口供了五年,BSCI A 级(是的 A 级,不是 C)拿了两年,去年开始接 Shein 的柔性单,小…

2026/10/16 20:21:49 阅读更多 →

最新新闻

MySQL学生成绩管理系统数据库设计:需求分析、建表、视图与存储过程完整方案

MySQL学生成绩管理系统数据库设计:需求分析、建表、视图与存储过程完整方案

简介:这份资源是北邮研一数据库课程大作业的完整详解文档,面向正在修读数据库系统课程、需要完成课程设计的研究生及高年级本科生。内容围绕学生成绩管理系统展开,覆盖需求分析、数据库设计、ER图绘制、逻辑结构设计与建表程序等完整流程&…

2026/10/17 16:15:27 阅读更多 →
Unity运行时导入3D模型:基于TriLib源码的模型加载与替换实践

Unity运行时导入3D模型:基于TriLib源码的模型加载与替换实践

简介:这套基于 Unity3D C# 与 TriLib 插件实现的运行时 3D 模型导入加载源码工程,面向需要为游戏或应用添加实时模型替换、关卡/场景编辑器以及 AR/VR 可视化等功能的 Unity 开发者。压缩包共 879 个文件,约 26.37MB,以 C# 脚本、…

2026/10/17 16:15:27 阅读更多 →
数据库关系代数与SQL实战入门:从课本习题到可执行查询

数据库关系代数与SQL实战入门:从课本习题到可执行查询

简介:本资源是《数据库系统概论(第5版)》王珊、萨师煊主编教材的配套课后习题详细答案,面向高校计算机、软件工程、信息管理等专业本科生及数据库初学者,用于巩固关系模型、关系代数、SQL语言等核心理论知识&#xff0…

2026/10/17 16:15:27 阅读更多 →
Unity3D数字孪生案例:从模型导入到数据驱动的完整落地路径

Unity3D数字孪生案例:从模型导入到数据驱动的完整落地路径

简介:面向数字孪生初学者与全栈开发爱好者的一套 Unity3d(WebGL)小案例代码包,聚焦硬件终端、webGL、Web端与服务端之间的数据交互链路,帮助解决 Unity3d 建模在网页端运行及 IoT 数据回流三维场景的问题。压缩包共3个…

2026/10/17 16:15:27 阅读更多 →
WordPress故障排查实战:从白屏、数据库到迁移陷阱一次说清

WordPress故障排查实战:从白屏、数据库到迁移陷阱一次说清

说起WordPress问题,我这几年遇到最多的不是某个单一故障,而是一堆"莫名其妙"的现象:前台白屏、后台登录死循环、文章保存按钮转圈、图片传不上去、网站被搜索引擎收录了一堆垃圾页面、搬家之后样式全丢。很多朋友的第一反应是"…

2026/10/17 16:15:27 阅读更多 →
Vim+GCC+库文件:打通Linux开发工具链的完整闭环

Vim+GCC+库文件:打通Linux开发工具链的完整闭环

我见过太多刚切到 Linux 的开发者,第一周就被劝退。倒不是 C 语言难,而是卡在了工作流上:在终端里不知道怎么快速改代码,写完程序不知道怎么编译,编译完了遇到各种链接报错又一头雾水。说白了,就是 Vim 操作…

2026/10/17 16:14:27 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/16 14:23:36 阅读更多 →