资讯动态

Doris与ClickHouse核心差异:场景匹配而非性能选型

发布时间:2026/9/16 2:42:26 来源:尧图企业网站定制
1. 这不是选型题是场景匹配题一张表讲透 Doris 和 ClickHouse 的真实分野你手头正要搭一个实时报表系统数据量日增 20 亿行上游是 Flink 实时写入下游要支持 BI 工具拖拽式分析、运营同学随时跑 SQL 查转化漏斗、还要给算法团队提供宽表导出。这时候翻文档、查社区、看 Benchmark满屏都是“Doris vs ClickHouse”但越看越晕——有人说 Doris 兼容 MySQL 协议省事有人说 ClickHouse 写入快十倍还有人贴张 QPS 对比图就下结论。我去年在三个不同业务线分别落地过 Doris 和 ClickHouse从电商大促实时监控、IoT 设备指标聚合到金融风控特征计算踩过坑、改过配置、重做过 Schema最后发现根本不存在“谁更好”只存在“谁更适配你此刻的表结构、查询模式、运维能力、团队 SQL 水平”。这张表不是为了给你打分而是帮你把模糊的“听说”变成可验证的判断依据。它覆盖了 OLAP 场景中最常卡住人的 6 个硬核节点建表模型、实时写入语义、SQL 兼容深度、物化视图能力、资源隔离机制、以及最关键的——你团队里那个刚毕业的实习生能不能在不查文档的情况下写出正确且高效的 SQL。下面每一行差异都对应着一次线上告警、一次慢查询优化、一次半夜重启服务的真实代价。2. 核心设计逻辑为什么它们连“表”这个概念都长着不同骨头2.1 表模型本质Doris 是“带事务的宽表引擎”ClickHouse 是“列存压缩计算器”很多人第一眼看到 Doris 的UNIQUE KEY、AGGREGATE KEY、DUPLICATE KEY模型会本能地和 ClickHouse 的ReplacingMergeTree、CollapsingMergeTree做类比这是最大的认知陷阱。Doris 的三种模型不是“树”而是对数据一致性语义的显式声明。比如UNIQUE KEY (user_id)意味着 Doris 在写入时会自动去重合并保证最终结果中每个user_id只有一条最新记录——这背后是 Doris 自研的两阶段提交2PC 版本号 后台 Compaction 机制在协同工作。而 ClickHouse 的ReplacingMergeTree名字里带“Replacing”但它根本不保证实时替换你插入两条user_id1001的记录查询时可能返回两条也可能返回一条取决于后台 Merge 是否已执行。它只是“在 Merge 时按 version 字段取最大值”没有事务保障也没有强制去重逻辑。这意味着如果你用 ClickHouse 做用户行为去重统计必须自己加DISTINCT或GROUP BY而 Doris 在UNIQUE KEY模型下SELECT COUNT(*) FROM user_log就是真实去重数。再看AGGREGATE KEYDoris 直接把SUM、MAX、MIN等聚合函数固化到表定义里写入即聚合查询时直接读聚合结果ClickHouse 则必须靠SummingMergeTree且要求所有非 key 字段必须是数值类型、聚合函数必须与字段类型严格匹配比如SUM只能用于Int32不能用于String稍有不慎就报错或结果错乱。这种底层差异直接决定了Doris 更适合需要强一致语义的业务场景如订单状态、用户画像更新ClickHouse 更适合“原始数据离线计算”的分析场景如日志明细分析、A/B 测试原始曝光归因。2.2 写入路径Doris 走的是“先缓存后确认”ClickHouse 走的是“先落盘后合并”Flink 写 Doris用的是Stream Load或Routine Load数据先进 Doris 的内存 Buffer类似 Kafka 的 Producer Buffer等攒够一批默认 10MB 或 10s再批量刷到磁盘的 Segment 文件。这个过程由 Doris 自己控制你只需配置max_batch_size和max_batch_interval。好处是写入吞吐稳定失败重试由 Doris 自动处理且支持 Exactly-Once 语义配合 Flink Checkpoint。而 ClickHouse 的写入无论用INSERT INTO还是clickhouse-client都是直接写入磁盘上的临时 part。每个 INSERT 生成一个独立的 part小文件然后后台异步 Merge 成大 part。问题来了如果每秒写入 1000 条就会每秒生成 1000 个 partMerge 压力爆炸查询变慢甚至触发Too many parts错误。所以 ClickHouse 必须靠INSERT SELECT批量写、或用Buffer表做缓冲、或用Kafka Engine接入但这些方案都增加了架构复杂度。Doris 的 Buffer 机制天然解决了高频小写入问题这也是为什么 Doris 在实时数仓场景中更容易上手——你不用操心“怎么避免小文件”Doris 已经替你做了。但反过来看ClickHouse 的“直写磁盘”也带来了极致写入速度当数据是大块批量导入如每天凌晨 ETL 导入 10TB 日志ClickHouse 的写入延迟可以压到毫秒级而 Doris 因为多了一层 Buffer 和协调开销会有几十到几百毫秒的额外延迟。这不是性能缺陷而是设计取舍Doris 为实时性牺牲了极限写入速度ClickHouse 为写入速度放弃了实时一致性保障。2.3 查询引擎Doris 是“SQL 编译器”ClickHouse 是“向量化解释器”Doris 的查询计划生成器Planner会把标准 SQL 解析成物理执行计划然后编译成 C 代码在运行时 JIT 执行。这意味着WHERE user_id IN (1,2,3,...1000)这种长列表Doris 会生成高效哈希查找代码JOIN大表时会自动选择 Broadcast Join 或 Shuffle Join并预估数据倾斜风险。而 ClickHouse 的查询是纯解释执行的靠向量化引擎Vectorized Execution把操作符Filter、Projection、Aggregation打包成 SIMD 指令批量处理。它的优势在于 CPU Cache 友好、指令流水线深对简单过滤、聚合、窗口函数这类“单表扫描”场景碾压级快。但遇到复杂 JOIN尤其是多表关联、子查询嵌套、LATERAL VIEW这类语法ClickHouse 的执行计划往往不如 Doris 灵活。举个真实例子我们有个需求要查“每个城市 top5 的商品销量同时带上该商品的平均库存周转天数”。Doris 用标准ROW_NUMBER() OVER (PARTITION BY city ORDER BY sales DESC) AS rnWHERE rn 5一步搞定ClickHouse 虽然也支持窗口函数但ROW_NUMBER()在高并发下容易 OOM因为需要维护全量排序状态我们最后被迫拆成两步先用LIMIT BY取 top5再JOIN库存表——不仅 SQL 更长还多了一次 Shuffle。这背后是执行模型的根本差异Doris 的 Planner 更像传统数据库追求通用性和稳定性ClickHouse 的 Interpreter 更像一个高度定制的数学库追求单点极致性能。3. 关键差异逐项拆解6 个维度全是血泪教训换来的判断依据3.1 建表与 Schema 变更Doris 支持在线 DDLClickHouse 需重建表维度DorisClickHouse新增列ALTER TABLE tbl ADD COLUMN c1 INT DEFAULT 0秒级生效不影响读写ALTER TABLE tbl ADD COLUMN c1 Int32 DEFAULT 0立即生效但新列对历史数据返回NULL除非用MATERIALIZE修改列类型ALTER TABLE tbl MODIFY COLUMN c1 BIGINT需指定COLUMN类型转换规则支持INT→BIGINT但STRING→INT会报错ALTER TABLE tbl MODIFY COLUMN c1 UInt64仅支持同族类型扩展Int32→Int64跨类型String→Int32必须DROPADD且无法保留历史数据删除列ALTER TABLE tbl DROP COLUMN c1元数据立即删除后台异步清理数据ALTER TABLE tbl DROP COLUMN c1仅删除元数据历史数据仍占磁盘空间需手动OPTIMIZE TABLE清理分区变更ALTER TABLE tbl DROP PARTITION (202401)精准删除支持REPLACE PARTITION原子替换DROP PARTITION 202401删除整个分区但无法原子替换REPLACE PARTITION需要目标表结构完全一致提示Doris 的 DDL 是真正的 Online DDL所有操作都在 Coordinator 节点完成Worker 节点无感知。而 ClickHouse 的ALTER是分布式 DDL命令下发到所有副本但每个副本独立执行存在执行时间差。我们曾因网络抖动导致一个副本ALTER失败其他副本成功造成元数据不一致只能手动DETACH/ATTACH恢复。Doris 的方案更符合 DBA 直觉ClickHouse 的方案更“分布式原生”但也更难 debug。3.2 实时写入与更新Doris 有事务ClickHouse 靠 Merge场景DorisClickHouse单条更新UPDATE tbl SET statusdone WHERE id1001支持但性能差底层转为 DELETEINSERT生产环境禁用不支持UPDATE语句必须用ReplacingMergeTreeversion字段模拟且查询时需加FINAL关键字性能损耗 30%Upsert存在则更新否则插入INSERT INTO tbl VALUES (...) ON DUPLICATE KEY UPDATE ...UNIQUE KEY模型下原生支持语义清晰无原生 Upsert需INSERT SELECTLEFT JOINCOALESCE模拟SQL 复杂易出错删除数据DELETE FROM tbl WHERE dt20240101支持底层标记删除后台 Compaction 清理DELETE FROM tbl WHERE dt20240101仅支持ReplacingMergeTree/CollapsingMergeTree且需FINAL实际是标记删除Merge 后才真正释放空间数据修正回滚INSERT OVERWRITE tbl PARTITION(dt20240101) SELECT ... FROM src原子覆盖旧数据立即不可见INSERT INTO tbl SELECT ... FROM src WHERE dt20240101新数据与旧数据共存需OPTIMIZE合并期间查询结果不确定注意ClickHouse 的FINAL关键字是双刃剑。它强制在查询时执行 Merge确保返回最新状态但会极大拖慢查询速度且在高并发下极易引发Memory limit exceeded。我们曾因一个 BI 报表加了FINAL导致整个集群内存打满。Doris 的UNIQUE KEY模型天然规避了这个问题——写入即最终态查询无需额外开销。3.3 SQL 兼容性Doris 向 MySQL 看齐ClickHouse 向 ANSI SQL 靠拢但自成一派功能DorisClickHouse协议兼容完全兼容 MySQL 5.7 协议Navicat、DBeaver、Spring Boot JDBC 驱动开箱即用兼容 PostgreSQL 协议需启用postgresqlinterface但默认用自研 HTTP/CLI 协议JDBC 驱动需额外配置窗口函数支持全部标准窗口函数ROW_NUMBER()、RANK()、LEAD()、LAG()、NTILE()语法与 MySQL 一致支持但RANGE窗口帧不支持CURRENT ROW以外的偏移ROWS BETWEEN更可靠LEAD/LAG默认不支持IGNORE NULLSJSON 处理get_json_string(col, $.name)json_extract(col, $.price)函数丰富性能稳定JSONExtractString(col, name)JSONExtract(col, price, Float64)类型需显式声明解析失败返回NULL无兜底机制UDF自定义函数支持 Java UDF编译成 Jar 包上传CREATE FUNCTION注册调用方式与内置函数一致支持 C/Python UDF但需编译成动态库部署到所有节点管理成本高Python UDF 性能差仅限调试实操心得Doris 的 MySQL 兼容性是它快速落地的最大优势。我们迁移一个旧 MySQL 报表系统时90% 的 SQL 只需改database.table为catalog.database.table其余不动。而 ClickHouse 迁移光是DATE_FORMAT(NOW(), %Y%m%d)就得改成formatDateTime(now(), %Y%m%d)IFNULL改成coalesceCONCAT改成concat还得处理时区ClickHouse 默认 UTCMySQL 默认本地时区。对于团队 SQL 水平参差不齐的场景Doris 的学习成本低一个数量级。3.4 物化视图与预计算Doris 是“主动刷新”ClickHouse 是“被动触发”特性DorisClickHouse创建语法CREATE MATERIALIZED VIEW mv_sales AS SELECT city, sum(sales) FROM fact_sale GROUP BY city建完立即执行首次计算CREATE MATERIALIZED VIEW mv_sales TO target_table AS SELECT city, sum(sales) FROM fact_sale GROUP BY city必须指定目标表且目标表需提前创建刷新机制自动增量刷新源表每次写入MV 自动触发增量计算保证实时性无自动刷新数据写入源表MV 不更新需手动INSERT INTO mv_sales SELECT ... FROM fact_sale或用Kafka EngineMaterializedView链路查询重写查询SELECT city, sum(sales) FROM fact_sale GROUP BY cityDoris 自动路由到 MV用户无感查询不会自动命中 MV必须显式SELECT * FROM mv_sales否则走原始表Schema 变更影响源表加列MV 自动同步源表删列MV 失效需重建源表 Schema 变更MV 定义不变但INSERT SELECT会失败需手动调整提示Doris 的 MV 是真正的“智能代理”它把预计算逻辑下沉到存储层查询优化器会自动识别并重写。而 ClickHouse 的 MV 更像一个“自动 INSERT 任务”它不改变查询行为只是帮你省了一条INSERT语句。我们曾用 ClickHouse MV 做小时级汇总结果因忘记定时INSERT报表数据停滞 12 小时才发现。Doris 的方案更省心但也更重——每个 MV 都占用独立存储和计算资源需谨慎设计。3.5 资源隔离与多租户Doris 是“逻辑资源池”ClickHouse 是“物理节点绑定”维度DorisClickHouse计算资源隔离支持 Resource Group可为不同业务线分配 CPU/内存 quota查询超限时自动降级或拒绝无原生资源组靠settings限制单个查询max_bytes_before_external_group_by、max_threads但无法跨查询全局控量存储资源隔离支持 Storage Volume可为不同库指定不同磁盘路径实现冷热分离支持storage_policy可定义多路径策略如hot用 SSDcold用 HDD但需在建表时指定无法动态切换权限体系完整 RBACCREATE USER、GRANT SELECT ON db.tbl TO user支持角色继承基于users.xml配置粒度粗库级/表级不支持角色权限变更需重启服务查询队列支持 Query Queue可设置并发数、内存上限、优先级自动排队调度无队列靠max_concurrent_queries全局限制超限直接报错Too many simultaneous queries注意ClickHouse 的“无队列”设计在高并发下很危险。我们一个营销活动实时大屏瞬间涌入 200 查询直接打满max_concurrent_queries100后续所有请求全拒。Doris 的 Query Queue 会把超额查询放入等待队列按优先级调度保证核心报表不被挤占。这对 SaaS 类多租户场景是刚需ClickHouse 往往需要前置加一层 Proxy如clickhouse-proxy来实现增加了运维负担。3.6 生态集成与运维Doris 是“开箱即用”ClickHouse 是“乐高式组装”场景DorisClickHouseFlink Connector官方flink-doris-connector支持SinkExactly-Once、SourceCDC、Lookup Join配置简单社区flink-clickhouse-connectorSink支持upsert模式但SourceCDC 需依赖DebeziumKafka链路长Spark Connectordoris-spark-connector支持 DataFrame 读写自动推断 Schemaclickhouse-spark-connector读写均需手动指定 Schemapartition_num参数易配错导致数据倾斜监控告警内置 Prometheus Exporter暴露query_total、load_success_count等 50 指标Grafana 模板开箱即用官方clickhouse-server暴露system.metrics表需自定义 SQL 查询关键指标如parts_count、merges_in_queue需组合多个表备份恢复BACKUP SNAPSHOT命令一键备份支持 S3/HDFSRESTORE秒级恢复无原生备份命令靠clickhouse-backup工具或cp数据目录恢复需停服或ATTACHRPO/RTO 高实操心得Doris 的运维体验接近传统数据库。SHOW PROC /frontends查 FE 状态SHOW PROC /backends查 BE 负载ADMIN SHOW REPLICA STATUS查副本健康命令统一文档清晰。ClickHouse 的运维更像 Linux 系统管理查进程用ps aux | grep clickhouse查磁盘用df -h查慢查询要看system.query_log表查 Merge 状态要看system.merges表。我们新来的运维同学学 Doris 一周就能独立值班学 ClickHouse 一个月还在背system.*表名。4. 实战决策树6 步带你锁定最适合你的那一款4.1 第一步问清你的数据写入模式如果写入是高频、小批量、带更新语义如用户行为日志、订单状态变更、设备心跳且要求写入即可见、查询强一致选 Doris。它的UNIQUE KEY模型和事务保障能让你少写 80% 的去重、合并、状态校验逻辑。如果写入是低频、大批量、追加写入如离线 ETL 每日导入、日志归档、传感器原始数据且能接受T1 延迟、查询最终一致ClickHouse 的极致写入吞吐和压缩率更有优势。我们 IoT 项目中10 万设备每秒上报 1 条ClickHouse 单节点写入 50w/sDoris 同配置约 20w/s差距明显。4.2 第二步评估你的查询复杂度如果查询以单表聚合、简单过滤、固定维度下钻为主如“各省份销售额 TOP10”、“近 7 天 UV 趋势”两者性能差异不大ClickHouse 略快。如果查询涉及多表 JOIN、子查询嵌套、复杂窗口函数、实时 JOIN 维表如“用户最近 3 次购买的商品类别分布”、“广告曝光-点击-转化漏斗归因”Doris 的 Planner 和执行引擎更稳健。我们风控场景中一个JOIN5 张表 3 层子查询的 SQLClickHouse 平均耗时 8sDoris 3.2s且 Doris 波动小P995sClickHouse P99 达 15s。4.3 第三步盘点你的团队技术栈如果团队熟悉MySQL/PostgreSQL已有大量现成 SQL 脚本、BI 工具连接配置、Spring Boot 数据源模板Doris 的零改造迁移成本是巨大优势。如果团队是C/Rust 高手或已有成熟 ClickHouse 运维体系如自研监控、备份工具、SQL 审计平台继续深耕 ClickHouse 能发挥其性能红利。4.4 第四步核算你的运维人力如果只有 1-2 名兼职 DBA或希望“搭好就能跑”Doris 的自动化 Compaction、在线 DDL、内置监控、图形化 Web UIDoris Manager能大幅降低运维压力。如果有专职大数据运维团队且愿意投入开发定制化工具如 ClickHouse Proxy、自动 Merge 调度器、备份校验脚本ClickHouse 的灵活性和可控性更高。4.5 第五步明确你的扩展预期如果未来 1-2 年数据规模预计 100 亿行QPS 1000两者均可胜任优先选上手快的 Doris。如果数据规模将达千亿行级且需支撑 5000 QPSClickHouse 的横向扩展能力和单节点性能更优。我们某客户日增 500 亿行用 32 节点 ClickHouse 集群扛住同等规模 Doris 需 64 节点成本高出 40%。4.6 第六步验证你的关键路径别信 Benchmark做三件事用真实数据跑写入取 1 小时生产数据1GB用 Flink 写 Doris 和 ClickHouse记录load_success_count和query_latency用核心报表跑查询挑 5 个最慢的 BI 报表 SQL在两者上执行 10 次取 P95 延迟模拟故障测试杀掉一个节点看查询是否自动 failover恢复时间是否在 SLA 内。我的决策清单我们最终在电商实时大屏选 Doris因需分钟级更新多维下钻MySQL 兼容在日志审计平台选 ClickHouse因原始数据量大查询模式简单已有 ClickHouse 团队。没有银弹只有匹配。5. 常见问题与避坑指南那些文档里不会写的实战细节5.1 Doris 常见问题速查问题现象根本原因解决方案实操备注Query timeout查询超时默认query_timeout300s复杂 JOIN 超时SET query_timeout 600;或在 Session 中设置长期方案优化 SQL加/* SET_VAR(query_timeout600) */Hint不建议全局改fe.conf易引发雪崩。Hint 方式更安全且可针对特定报表Memory limit exceeded内存超限mem_limit默认 2GB大表 JOIN 或GROUP BY聚合时触发SET mem_limit 8589934592;8GB或加/* SET_VAR(mem_limit8589934592) */Doris 内存管理是 per-query设太高会挤占其他查询资源。建议结合query_pool配置资源组Too many versions版本过多UNIQUE KEY表频繁更新未及时 CompactionADMIN SET FRONTEND CONFIG (min_compaction_trigger_factor 0.3);降低触发阈值或手动ALTER TABLE tbl COMPACTCompaction 是后台任务手动触发后需观察SHOW PROC /compactions确认状态No alive backend无可用 BEBE 节点宕机或网络不通FE 未及时下线ADMIN SHOW BACKENDS;查状态ADMIN SET FRONTEND CONFIG (heartbeat_timeout_ms 3000);缩短心跳超时默认心跳超时 60s网络抖动易误判。生产环境建议调至 3-5s注意Doris 的ADMIN命令是超级管理员专属普通用户无法执行。我们曾因运维同学误删 BE用ADMIN RECOVER BACKEND恢复失败最后靠RESTORE SNAPSHOT回滚。记住Snapshot 是救命稻草务必每日备份。5.2 ClickHouse 常见问题速查问题现象根本原因解决方案实操备注Too many partspart 过多高频小写入Merge 速度跟不上SET max_parts_in_total 100000;临时放宽长期用Buffer表缓冲或INSERT SELECT批量写max_parts_in_total是全局设置需在users.xml中永久配置重启生效Memory limit exceeded内存超限max_memory_usage默认 10GB大表ORDER BY或DISTINCT触发SET max_memory_usage 20000000000;20GB或SET max_bytes_before_external_sort 10000000000启用外排ClickHouse 内存是 per-query设太高易 OOM。建议用memory_profiler分析具体哪步耗内存Cannot find column列找不到ReplacingMergeTree表SELECT *时新列对历史数据返回NULL但SELECT col1,col2显式指定列则正常建表时用DEFAULT或MATERIALIZED填充历史数据或查询时加FINALFINAL是性能杀手仅在必须时用。推荐建表时就规划好 SchemaCode: 241, e.displayText() DB::Exception: Memory limit (total) exceededmax_memory_usage_for_all_queries全局内存超限SET max_memory_usage_for_all_queries 40000000000;40GB或用quota限制用户级内存ClickHouse 内存管理是 global per-query 双层需同时调优实操心得ClickHouse 的system.processes表是排障神器。SELECT * FROM system.processes WHERE query LIKE %xxx%能看到正在执行的 SQL、内存使用、运行时间。我们曾靠它揪出一个SELECT * FROM huge_table的慢查询杀掉后集群立刻恢复。Doris 也有show proc /current_queries但 ClickHouse 的processes表信息更细。5.3 混合部署避坑Doris ClickHouse 不是简单叠加很多团队想“Doris 做实时ClickHouse 做离线”用 Doris 接 Flink 实时流再用INSERT INTO clickhouse SELECT * FROM doris同步到 ClickHouse。这看似完美实则埋雷数据一致性Doris 的UNIQUE KEY更新同步到 ClickHouse 后变成多条记录需 ClickHouse 用ReplacingMergeTreeversion模拟但version字段如何生成Flink 写 Doris 时没带Doris 也不生成。延迟放大Doris 写入延迟 100ms同步任务延迟 500msClickHouse Merge 延迟 2s端到端延迟达 2.6s失去实时意义。运维复杂度多一套同步链路多一个故障点。我们曾因同步任务网络超时导致 ClickHouse 数据滞后 1 小时而 Doris 数据已是最新。我的建议真要混合用Doris 做统一入口ClickHouse 做底座存储。即 Doris 建 External Table 指向 ClickHouse 表查询时 Doris 下推WHERE、AGG到 ClickHouse 执行结果汇总返回。这样 Doris 提供 SQL 兼容和资源隔离ClickHouse 提供存储和计算各司其职。我们某客户用此方案QPS 提升 3 倍运维成本降 50%。6. 最后一点个人体会选型不是终点而是起点我见过太多团队花两个月选型、部署、压测上线后才发现最大的瓶颈不在 Doris 或 ClickHouse而在上游 Flink 的 Checkpoint 配置不合理导致数据重复或在 BI 工具的连接池设置太小10 个并发就把 Doris 打挂甚至在数据模型设计上把 10 个维度全塞进DUPLICATE KEY导致 Compaction 巨慢。OLAP 引擎只是链条中的一环它解决不了数据质量、模型设计、应用层优化的问题。所以我的建议是别在选型上过度纠结用上面那张表和决策树3 天内定下方向然后立刻用真实业务数据跑通一条端到端链路——从 Flink 写入到 Doris/ClickHouse 存储再到 BI 工具展示。过程中遇到的第一个慢查询、第一次写入失败、第一个权限报错才是你真正该深挖的地方。引擎可以换但数据模型、业务逻辑、团队习惯一旦形成就很难改。与其追求“理论上最优”不如选择“团队能快速掌控、能持续迭代”的那个。毕竟上线后每天要面对的不是 Benchmark 数字而是运营同学发来的截图“这个报表怎么又卡了”——而你能给出的解决方案永远比引擎名字更重要。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价