最近在做技术选型评审时,被问得最多的一句话是:我们有个实时报表需求,到底用 ClickHouse 还是 Doris?这问题背后往往隐藏着另一个更根本的疑问——实时数仓到底该长什么样。如果你也在这个路口犹豫过,这篇 Doris 全景解析应该能帮你省下不少时间。
这是 Doris 系列的开篇。我打算用一篇足够扎实的内容,把 Doris 的定位、架构、特性和选型逻辑一次讲清楚。熟悉我的读者知道,我不喜欢写那种"官网介绍复读机"式的文章,所以这篇会直接按我实际的认知路径来:先搞明白它是什么、拆开看它怎么工作、再判断什么业务该选它、最后给到从部署到日常运维的几个关键提醒。后续这个系列还会逐步展开部署实操、性能调优、实时数仓链路搭建、故障排查等主题,每一篇都会对应一个实战场景。
1. 先把定位弄清楚:Doris 不是另一个 MySQL,也不是又一个 Hive
1.1 一次架构评审里的"要不要用 Doris"
上个月帮一个团队做技术选型,业务规模大概是一天新增数亿条用户行为数据。他们原来的链路是 Flume 攒日志进 Hive,凌晨定时跑 Spark 任务生成报表,业务方第二天早上看 T+1 数据。时间一长,业务方坐不住了,活动运营要看分钟级漏斗,风控团队要实时分析设备指纹关联,风险团队要求查询响应在秒级以内。原有的离线数仓显然扛不住这种需求,于是团队内部出现了两种声音:一种说直接用 ClickHouse,查询快;另一种说用 Doris,毕竟是完整数仓。
这个场景特别典型,也是我经常遇到的。很多团队其实不是缺一个查询快的引擎,而是缺一个能承上启下的实时数仓:既要能接住实时写入的数据,又要能跑复杂的多表关联分析,还要兼容 MySQL 生态好让业务方平滑迁移。Doris 的定位恰好在这个位置——它是一个 MPP(大规模并行处理)架构的 OLAP 数据库,主打的就是"实时数仓"这个场景。它不是用来替代 MySQL 做交易系统的,也不是让你把整个 Hive 数仓推倒重来的,而是在离线数仓和实时业务之间搭一座桥。
1.2 OLAP 与 OLTP 的边界,先别急着夸它
很多人第一次接触 Doris 时容易犯一个错误:拿它的语法跟 MySQL 比,觉得"既然兼容 MySQL 协议,那是不是能用它扛线上业务?"不行。原因在于 Doris 底层是列式存储,数据组织方式、索引结构、执行引擎全是为分析型查询设计的。这种设计让它扫描几百 GB 数据做聚合、Join 时快得惊人,但代价是单条点查和写入场景远不如 MySQL 这类 OLTP 数据库。
我把这两类数据库的区别总结成一句话:OLTP 关心的是"一个用户的一笔订单现在怎么样了",OLAP 关心的是"一千万个用户本周的消费趋势是什么"。前者要求低延迟、强事务、高并发;后者要求高吞吐、大扫描、复杂计算。Doris 属于后者,而且它把"实时写入 + 分析查询"结合得比较好,所以叫实时数仓。如果你在做选型时发现需求是大量高并发点查,那应该去找 MySQL、PostgreSQL 或者 TiDB,而不是 Doris。
1.3 这个系列打算讲什么
因为这个是系列开篇,我先把计划交代一下,方便你按需阅读。本系列后续会拆成几块:部署篇会讲 FE/BE 的安装踩坑和生产配置;性能调优篇会专门讲慢查询、内存管理、Compaction 和表模型设计;数据接入篇会讲 Flink、Kafka、离线批量导入和 CDC 同步;最后还有一块是运维实战,包括 CCR 跨集群复制、监控告警、升级迁移等。今天这篇是地基,地基打不牢,后面全是空中楼阁,所以即便你急着想看部署,也建议先把架构部分读完。
2. 架构拆解:一个查询请求在 Doris 内部走过了哪些路
2.1 FE 与 BE:一个管脑袋,一个管手脚
Doris 的整体架构可以分成两个核心角色:FE(Frontend)和 BE(Backend)。如果你第一次接触,可以用一个粗浅的类比来记:FE 是大脑,BE 是手脚。
FE 负责所有"需要思考"的事情——接收客户端的 SQL、解析语法、生成执行计划、决定让哪些 BE 干活、干完活把结果汇总返回。它还负责元数据管理,也就是所有的库、表、分区、分桶、副本信息都记录在 FE 里。FE 可以横向扩展,多部署几个实例组成一个高可用集群,它们之间通过一种轻量级的 Raft 协议同步元数据日志,主 FE 挂掉之后会有 Follower 自动顶上。这点很重要,因为很多 OLAP 引擎的单点问题就出在元数据服务上,Doris 在这方面做得比较省心。
BE 负责所有"动手"的事情——真正把数据落盘、读取、过滤、聚合、Join。BE 节点是无状态的,可以随便扩容,数据通过多副本来保证高可用。FE 不做数据存储,BE 才是数据真正躺着的地方。日常运维中大部分人看到的问题是 BE 的磁盘满了、BE 挂了、BE 内存不够,FE 的问题相对少,但一旦 FE 出问题往往是集群级事故。
2.2 数据落盘:Tablet、分区分桶与列式存储
BE 里最核心的数据管理单位叫 Tablet。一张表的数据在物理上会被切分成很多个 Tablet,每个 Tablet 有若干个副本(生产环境通常 3 副本),每个副本是独立管理的。这个设计带来两个好处:一是查询可以并行扫多个 Tablet,天然适合 MPP;二是单个 Tablet 损坏不影响整张表,后台会自动修复副本。
表在逻辑上先按分区(Partition)切分,再按分桶(Bucket)切分。打个比方,分区相当于把一整年的数据按月份分成 12 个抽屉,分桶相当于每个抽屉里再按用户 ID 的哈希值分成 20 个小格子。查询时如果指定了时间范围,FE 能直接跳过不相关的分区,这叫分区裁剪;如果查询条件里带了分桶列的等值条件,Doris 还能进一步做到分桶裁剪,只扫命中的小格子。这是我做慢查询优化时最先检查的点,很多时候性能上不去就是因为表刚建的时候分区键和分桶键没设计好。
物理存储上,Doris 采用列式存储,每一列的数据独立存放在文件里。列式存储对分析型查询特别友好,因为它只需要读取查询涉及的列,不用像行式存储那样把整行数据都从磁盘拉出来。同时,数据在写入时会先在内存里形成 MemTable,攒够一批后一次性落盘成不可变的 Segment 文件,这个写路径跟 LSM-Tree 的思路很像,所以 Doris 能够支撑比较高频的实时写入。随后后台会做 Compaction,把小 Segment 合并成大 Segment,减少文件数量、清理被覆盖的老版本数据。这个机制后续在手动合并那一章还会细讲。
2.3 一次查询的完整旅程
一条 SQL 进来后会发生什么?我按自己的理解画了一条链路,你跟着走一遍就能理解整个架构是怎么协作的:
- 客户端通过 MySQL 协议连上 FE 的查询端口(默认 9030),把 SQL 发过去。
- FE 做词法、语法解析,生成逻辑执行计划,再经过基于成本的优化器(CBO)优化,生成物理执行计划。
- FE 把执行计划拆成多个 PlanFragment,按照数据分布情况分发到不同的 BE 上。
- BE 并行执行各自的 Fragment,扫描本地 Tablet 数据,做过滤、聚合、Join。中间涉及跨 BE 数据传输时会做 Shuffle。
- 各个 BE 把中间结果汇总回 FE,FE 做最终处理后返回给客户端。
这套机制的核心思想是 MPP:让所有 BE 同时干活,而不是一台机器累死。理论上 BE 节点越多,查询并行度越高,这也是 Doris 能撑住大量级数据扫描的原因。你不需要像使用 Presto 那样额外部署一个计算集群,Doris 的 FE + BE 本身就是存储计算一体的结构,部署简单很多。
2.4 索引和元数据:快速定位数据的底气
虽然 Doris 不是那种背着大量二级索引的数据库,但它在扫描路径上做了不少文章。理解这些索引,对你后面做建表设计和慢查询优化非常重要。
- 前缀索引(Sorted Index):表数据按排序键排序后,Doris 会为每 1024 行建立一个稀疏索引,只记录这 1024 行的起始值。查询时通过二分查找能快速定位到可能命中的数据块,跳过大量无关数据。
- ZoneMap 索引:每个数据文件中记录了每一列的最小值和最大值。查询时如果条件能直接排除整个文件的范围,那整个文件都会被跳过,不用真正读取。
- Bloom Filter:适合高基数列的等值过滤,能进一步减少 IO。算是一个可选的索引类型,需要建表时指定。
- Bitmap 索引:适合枚举值不多、基数较低的列,比如性别、城市、状态等,加快这类列的过滤和去重。
这些索引配合分区分桶裁剪,构成了 Doris 查询性能的第一道防线。很多人以为 Doris 快是因为 C++ 写的,其实更关键的是这些细致的存储层设计。执行引擎是最后一道加速器。
3. 特性全景:实时数仓场景下 Doris 的立身之本
3.1 三种数据模型怎么选:Duplicate、Unique、Aggregate
Doris 建表时要明确数据模型,这个选择直接决定后续的查询性能和写入逻辑。三种模型各有侧重,我帮你理清楚。
- Duplicate Key 模型:不处理任何数据合并,同一行数据可以重复保存。最适合明细数据,比如埋点日志、订单明细、操作记录。这种模型的写入完全无额外开销,查询也最灵活,是默认推荐。
- Unique Key 模型:主键唯一,新写入的数据按主键覆盖旧数据。适合维度表和需要 upsert 的场景,比如用户表、商品表。Doris 1.2 以后默认开启 Merge-on-Write,主键更新在写入阶段就完成,查询时不需要实时合并,性能比早期的 Merge-on-Read 稳定得多。实时数仓里最常用的建模方式就是 Unique Key。
- Aggregate Key 模型:按聚合列对数据进行预聚合,比如求和、取最大值、去重计数。写入时相同聚合键的数据会立刻合并,存储量小、查询快,适合指标看板、分时汇总。缺点是你只能查预聚合的粒度,明细查不了。
实际建模时,我习惯用一句话做初步判断:明细留 Duplicate,变化覆盖留 Unique,统计指标留 Aggregate。很多团队一上来就把所有表都建成 Unique Key,结果发现 Flink 写入时因为需要处理主键更新而拖慢了吞吐,这不一定是 Doris 的错,很可能是模型选错了。另外,如果你用 FlinkSQL 往 Unique Key 表里写数据,要特别关注写入并发和乱序问题,后面第 5.4 节我会专门说。
3.2 导入生态:Stream Load / Broker Load / Routine Load
Doris 的导入方式很丰富,这也是它作为实时数仓的巨大优势。我按实际使用频率梳理一下:
- Stream Load:最常用的同步导入方式,通过 HTTP 接口提交数据,适合业务代码实时写入。很大带宽下性能不错,用法简单,一条 curl 命令就能搞定。
- Broker Load:适合大批量离线导入,比如从 HDFS、S3、OSS 上把文件拉进 Doris。Broker 进程负责协调数据分发,适用于 T+1 补数和初始化全量数据。
- Routine Load:Doris 原生支持订阅 Kafka 数据流,持续不断地消费消息并写入表中。对于实时数仓来说,这几乎是最省心的一条链路——不需要自己维护 Flink 任务,直接在 Doris 里建一个 Routine Load 就能让 Kafka 数据源源不断进表。
- Flink Connector:如果你已经在用 Flink 做实时计算,可以借助 Doris 官方 Sink,支持两阶段提交实现精确一次语义,适合复杂 ETL 后的结果写入。
四种方式覆盖了"实时、批量、流式、计算后写回"这四类需求,这一点让 Doris 很容易融入现有数仓体系。我在多个项目里都是这么设计的:Kafka 原始数据用 Routine Load 进 ODS 层明细表,Flink 做实时计算后用 Connector 写 DWD/DWS 层结果表,历史数据用 Broker Load 从 Hive 或对象存储初始化。这一套下来,实时链路和离线链路都跑在 Doris 上,管理成本比同时维护两套系统低得多。
3.3 查询能力:SQL 兼容、Join 优化和使用边界
Doris 对 SQL 的支持在 OLAP 引擎里算是非常完整的,兼容 MySQL 协议,这意味着你的数据可视化工具(比如帆软、Tableau、Superset)、BI 报表、Java 后端都能直接用 MySQL 驱动连上来,应用改造成本相当低。
在多表关联分析上,Doris 有 Colocate Join、Bucket Shuffle Join、Runtime Filter 等优化。其中 Colocate Join 是最值得理解的一个概念:如果两张表的分桶键和分桶数完全一致,Doris 能让本地数据直接做 Join,不需要跨节点 shuffle,效果立竿见影。我通常会建议把高频 Join 的两张表设计成相同的分桶策略,这是调优性价比最高的事。
不过 Doris 也有明确的使用边界。它不适合做事务型工作负载,不支持跨行跨表事务;高并发低延迟点查不是它的主场,单秒几百上千的简单查询可能还行,再上去就吃力了;复杂递归查询、图计算这些也最好交给专门的引擎。你要是在选型阶段把这些需求带进来,多半会失望。
3.4 CCR 与监控:一个成熟数仓该有的东西
一个数仓能不能长期用下去,除了查询性能,还要看运维能力。Doris 有两个容易被忽略但很关键的能力点,我提一下。
第一个是 CCR(Cross Cluster Replication),官方配套的 CCR Syncer 工具可以实现跨集群的库表级复制。我遇到过两个典型场景:一是机房级别容灾,主集群和灾备集群之间做异步复制;二是读写分离,实时查询走主集群,分析任务连到只读副本集群,避免大查询拖垮线上。CCR 原理上类似数据库的 Binlog 同步,主集群的变更会被持续同步到从集群。当然它不像主从复制那样能做到秒级以下延迟,使用时要评估业务对延迟的容忍度。
第二个是可观测性。Doris 支持导出监控指标到 Prometheus,再配合 Grafana 能展示 FE/BE 的 CPU、内存、磁盘 IO、查询延迟、Compaction 状态等指标。我建议一上生产就先把监控搭起来,不要等出了问题才想起来看。FE 的审计日志也会记录每一条 SQL 的执行时间,这是后续做慢查询分析的重要数据源。
4. 选型指南:什么业务适合上 Doris,什么场景千万别硬扛
4.1 适合 Doris 的场景
结合我参与过的项目,Doris 表现最好的场景集中在下面几类:
- 用户行为分析:埋点日志、PV/UV、漏斗转化、留存分析,这是 Doris 最常见的主场。原因很简单,数据量大、查询固定、聚合多、明细多。
- 实时报表和展示看板:从 T+1 升级到分钟级甚至秒级。Routine Load 接 Kafka,Doris 实时落表,BI 直接可视化,链路短且稳定。
- 多表关联的复杂分析:比如订单、商品、用户三张表 join 后做维度下钻分析。Doris 的 CBO 优化器和 Colocate Join 能处理得比较从容。
- 数据湖加速查询:Doris 支持通过 Catalog 映射 Hive、Iceberg、Hudi 等外部数据源,把高频查询的数据加速到 Doris 内部,减少直接扫数据湖带来的高延迟和高成本。
如果你发现自己正好属于这几类,Doris 大概率值得认真考虑。
4.2 不适合 Doris 的场景
有些场景是我明确不建议用的,选错的话后期运维会很痛苦:
- 强事务场景:电商下单、订单状态流转、账户余额变更。因为 Doris 不提供完整的事务支持,也没有行级强一致读写,这类业务应该留给 MySQL 或 TiDB。
- 超高并发点查:比如用户每次打开 App 都查询一次最新状态。这种请求再小,量上来以后 Doris 的列式存储和 CPU 开销也吃不消。
- 需要灵活更新单行的 OLTP 应用:虽然 Unique Key 支持更新,但本质是为批量导入设计的,不能把它当作行存储数据库来用。
- 超大规模明细的全量交互式查询:比如几百 TB 到 PB 级数据全部存在对象存储,主要靠 Trino/Spark 做临时探索,Doris 更适合存热数据或汇总数据,把大海量冷数据全搬进来成本太高。
4.3 四款主流 OLAP 引擎的横向对比
每次选型都会遇到 ClickHouse、StarRocks、Kylin 这些名字,我把关键差异按自己的视角总结成一张表:
| 维度 | Doris | ClickHouse | StarRocks | Kylin |
|---|---|---|---|---|
| 核心定位 | 实时数仓,存储计算一体 | 列式 OLAP 查询引擎 | 实时数仓(Doris 同源分支) | 预计算 OLAP,Cube 加速层 |
| 多表 Join | 强,有 Colocate Join、Bucket Shuffle | 较弱,大 Join 容易爆内存,需要人工优化 | 强,继承并扩展了 Join 优化 | 不支持直接 Join,依赖预建模 |
| 数据更新 | Unique Key 模型支持 Merge-on-Write | 更新能力弱,更新场景不友好 | 支持 Unique Key 和主键表 | 更新代价高,需重建 Cube |
| 实时写入 | Routine Load、Stream Load、Flink Connector | 默认 MergeTree,高频写入需批量 | 同 Doris,生态完善 | 通常走批式导入 |
| 索引加速 | 前缀索引、ZoneMap、Bitmap | 主键稀疏索引、跳数索引 | 同 Doris | 预聚合 Cube 提供极速查询 |
| 易用性 | 兼容 MySQL 协议,部署简单 | SQL 方言有差异,学习成本中 | 同 Doris | 依赖 Hadoop 生态,较重 |
这张表不是要分个高下,而是帮你理解各自的设计哲学。ClickHouse 的强项是单表聚合性能和极致的查询速度,但多表 Join 需要你小心伺候;StarRocks 和 Doris 算是同根生,功能和定位非常接近,选择更多取决于团队已有积累和社区生态;Kylin 适合查询模式非常固定、对延迟极其敏感的 BI 加速场景,因为它是"查询前就把结果算好",灵活性差一些。
4.4 选型决策清单
我建议你拿着下面这份清单,去问业务方和开发团队,答案会直接指引你走哪条路:
- 查询类型:是复杂的多表关联分析,还是单表聚合为主?关联多选 Doris/StarRocks,单表聚合快选 ClickHouse。
- 数据更新频率:每天是否有大量 upsert?有更新选 Unique Key 能力强的引擎。
- 实时性要求:是秒级/分钟级实时,还是 T+1 就够?实时选 Doris,纯离线选 Hive+Trino 也能解决。
- 查询并发:同时有多少报表在查?并发高要考虑 FE 资源和查询队列配置,此时 Doris 比 ClickHouse 容易控制。
- 团队技术栈:有没有 Java 服务可以低成本接入 MySQL 协议?Doris 的协议兼容性会省很多事。
- 运维成本:你能养几个大数据组件?Doris 一个集群解决存储和计算,Kylin 那套 Hadoop 生态运维成本明显更高。
5. 快速起步:部署和连接阶段的高频坑位预警
5.1 部署环境:能用 Linux 就别在 Windows 上硬刚
Doris 官方对 Linux 的支持最完善,生产环境请直接用 CentOS 7+、Ubuntu 16.04+ 这类系统。很多人问我能不能在 Windows 上部署,我的回答是:能跑通 on Docker,但别指望原生部署很顺利。因为 Doris 的 BE 是 C++ 编译的二进制,依赖 glibc 和一堆 Linux 系统库,Windows 上容易遇到环境兼容问题,即便以前的版本能在 Cygwin 里跑,也不建议在生产上冒险。
如果你只是想本地体验一下,最省事的办法是装 Docker Desktop,用官方镜像起一个单节点 Doris 集群。注意容器方式部署时不要忘记映射 FE 和 BE 的关键端口,比如 8030(FE WebUI)、9030(FE MySQL 查询口)、8040(BE WebServer)、9050(BE 心跳)、9060(BE 数据端口)。第一次启动时建议用docker logs盯着 FE 和 BE 的日志,确认没有报错再连。
5.2 FE / BE 的启动顺序和首连方式
如果是物理机或虚拟机部署,下载官方二进制包解压之后,步骤大致是这样:
- 先配置 FE,修改
fe/conf/fe.conf,至少确认meta_dir有地方放元数据,内存参数按机器规格设置。 - 启动 FE:
sh bin/start_fe.sh --daemon。首次启动会初始化元数据。 - 再用 MySQL 客户端连接 FE:
mysql -h127.0.0.1 -P9030 -uroot,执行SHOW PROC '/frontends'确认 FE 状态正常。 - 配置 BE,修改
be/conf/be.conf的storage_root_path,也就是数据存放目录。 - 启动 BE:
sh bin/start_be.sh --daemon。 - 回到 MySQL 客户端,执行
ALTER SYSTEM ADD BACKEND "127.0.0.1:9050";把 BE 注册进集群。 - 执行
SHOW PROC '/backends',如果 BE 状态显示Alive: true,集群就通了。
整个过程不算复杂,但有两个坑容易踩:一是 FE 和 BE 的时钟必须同步,时钟漂移会引发各种诡异问题,部署前把 NTP 配好;二是首次启动 FE 后不要急着关进程,元数据初始化需要点时间,看到日志里输出 successful 字样才算完。网上有很多人问为什么起不来,十有八九是端口被占用或者目录权限不对。
5.3 JDBC 连接 Doris:超时配置别只盯着客户端
Doris 兼容 MySQL 协议,所以 Java 后端直接用 MySQL Connector/J 就能连。连接串大致长这样:
jdbc:mysql://fe_host:9030/your_db?connectTimeout=10000&socketTimeout=60000&rewriteBatchedStatements=true但我要提醒你,连接超时问题往往是两层叠加的结果。第一层是客户端链路,connectTimeout和socketTimeout设置不当,网络抖动时会出现连接卡死或请求超时;第二层是 Doris 服务端的query_timeout,它是一个会话级变量,默认 300 秒,如果集群负载高或者查询复杂,超过这个时间会被 FE 主动 cancel,前端看起来就是"连接超时"。
所以排查超时问题时,先看 FE 审计日志里这条 SQL 是否真的执行完了、执行了多久,再看客户端报的异常是连接建立阶段还是读取阶段。很多时候不是 Doris 查询慢,而是 JDBC 驱动等待时间设得太短。你可以在数据库连接池初始化时执行一句SET query_timeout = 600,或者对特定慢查询任务单独设置,这样比全局调大更合理。Spring Boot 项目里如果用了 HikariCP,注意connection-timeout和max-lifetime也要跟 Doris 的会话超时匹配,不然连接池里的连接被 Doris 回收后,客户端还在用就会报通信链路异常。
5.4 Flink 写入 unique key 表的注意事项
实时数仓链路里,FlinkSQL 写 Doris 是很常见的姿势。如果你要写入的表是 Unique Key 模型,有几点必须注意。
首先是语义选择。Flink Doris Connector 支持 TwoPhaseCommit,能做到精确一次,但由于 Doris 本身不支持跨行事务,精确一次是针对单批次导入而言的,你要理解这个边界。其次是写入并发。往 Unique Key 表写数据时,如果 Flink 的并发度过高,容易触发太多小事务,BE 端 compaction 压力会变大;如果并发度过低,吞吐又上不去。我一般从 4 到 8 个并发开始压测,根据 BE 的 CPU 和导入延迟再调整。还有一点容易被忽略:Flink 任务重启后,上游数据可能会重放,Doris 的 Unique Key 模型靠主键去重,但前提是主键设计得对,如果你把非唯一字段也放进了主键,重复数据会一直堆着。
另外,如果你在文档里看到"union key",大概率是 unique key 的笔误,别在建模时被带偏了。Unique Key 的含义是主键唯一、更新覆盖,不是"联合主键"的意思。
6. 日常运维:慢查询定位与手动合并的实操经验
6.1 慢查询定位:从审计日志到 Profile
Doris 上线之后,日常最常做的事就是查慢查询。我推荐的排查路径是自顶向下:
- 打开 FE 审计日志(
fe/log/fe.audit.log),按执行时间倒序找到耗时较长的 SQL。审计日志里记录了查询用户、客户端 IP、SQL 语句、执行耗时、返回行数等关键信息。 - 对可疑 SQL 执行
EXPLAIN,看执行计划是否命中了分区裁剪、分桶裁剪,Join 方式是否合理,有没有出现全表扫描。 - 更进一步,可以开启查询 Profile。Doris 1.2 以后可以用
SHOW QUERY PROFILE查看最近查询的详细执行指标,能看到每个 BE 上 Scan 耗时、Exchange 耗时、算子耗时占比。
实际遇到最多的慢查询原因,我总结成三种:第一种是查询条件没有包含分区列,导致全分区扫描,比如时间范围没传;第二种是 Join 字段不是分桶键,导致数据需要跨节点 Shuffle,量大时很容易慢;第三种是表模型和物化视图不匹配,明明可以走 Rollup 预聚合,结果查了明细表。这三种问题的共性都是设计阶段能避免的,所以我在建表评审时都会反复确认分区键、分桶键和查询模式是否对齐。
6.2 手动触发对表的合并:原理与场景
Doris 后台的 Compaction 是自动执行的,正常情况下你不用管。但有些场景下,自动合并不一定能及时跟上,比如高峰期大量小文件导入,或者某张表长期高频更新产生了大量版本,这时候查询可能要读取很多小文件和旧版本数据,性能明显下滑。此时手动触发对表的合并就很有必要了。
Doris 支持手动触发 Compaction,触发方式在不同版本里有些差异,大体上是一条类似ALTER TABLE table_name COMPACT;的语句。因为版本迭代较快,我建议使用前先在 MySQL 客户端里执行HELP ALTER TABLE;看看当前版本的语法说明。手动触发后,可以用SHOW PROC '/statistic';或相关系统表观察该表的版本数量和合并进度,确认 Compaction 是否把文件数压了下来。
但我要强调,手动合并不是日常调优的常态手段。它更适合两类场景:一是你正在做 compaction 参数调优,想验证不同配置对查询性能的影响;二是某张表出现异常,比如版本堆积严重、查询变慢,需要人工干预。如果一张表长期需要手动合并才能维持性能,你应该回头检查写入模式和模型设计,而不是每次都手动救火。
6.3 三个踩过之后才会记住的坑
最后分享三个我在实际项目里踩过、而且很多人都会踩的坑,希望能让你少走弯路。
第一个坑是分桶数拍脑袋。建表时分桶数设太大,会产生大量小 Tablet,元数据压力大,查询调度也慢;设太小,数据倾斜严重,并行度上不去。我现在的做法是先用数据量预估每个分桶 2-4 GB 左右,上线后再根据实际查询表现调整。Doris 支持动态修改分桶数,不用怕建错,但设计时多花十分钟能省掉后面的大调整。
第二个坑是排序键和查询条件不匹配。Doris 数据按排序键有序,如果你高频查询的过滤列不是排序键的前缀列,前缀索引可能完全用不上,查询只能靠 ZoneMap 碰运气。建表之前,把业务方最常用的 where 条件列出来,把区分度高且等值过滤多的列放到排序键前面,这个收益比任何调优参数都大。
第三个坑是导入并发无节制。Stream Load 虽然好用,但如果你开了几十个并发同时灌数据,BE 的 CPU 和磁盘 IO 会被瞬间打满,正常的实时查询反而被拖垮。建议给导入任务设置合理的并发上限,并严格区分高峰期和低峰期:高峰期只跑核心实时导入,大批量补数任务挪到凌晨。
实时数仓这条路,Doris 只是工具箱里的一件趁手工具,真正决定成败的还是你对业务查询模型的理解和数据建模的功力。选型阶段多花点时间想清楚,后面几年都会省心很多。下一篇我会专门讲生产环境部署的完整步骤和参数调优,到时候再展开聊。