基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

在使用 Doris的过程中,都遇到了哪些问题,又是如何调优解决的?

在 Apache Doris 的实际生产使用中,随着数据量的增长和业务复杂度的提升,通常会遇到写入瓶颈、查询延迟、内存溢出(OOM)、以及集群稳定性等方面的问题。

以下总结了在使用 Doris 过程中最常见的痛点问题、根本原因分析,以及相应的调优和解决方案。


一、 写入与导入(Ingestion)相关问题

1. 现象:写入频繁报错 Too many segmentsOLAP_ERR_PUSH_VERSION_ALREADY_EXIST

  • 原因分析
    • 小批次高频写入:Doris 的 LSM-Tree 存储结构决定了每次导入都会生成一个 Rowset(包含多个 Segment 文件)。如果使用 Flink 或 Spark 写入时,攒批太小、频率太高,会导致后台 Compaction(合并文件)的速度赶不上写入的速度,进而导致版本数(Version)超限(默认最大 1000)。
  • 解决方案与调优
    1. 攒批写入(最重要):调整 Flink/Spark Connector 的配置,增加写入时间间隔(如 5s~10s)和单批次数据量(如几十万行或几百MB)。
    2. 调整 BE Compaction 参数(提升合并速度):
      plaintext
      # be.conf
      # 提高 Compaction 的线程数
      cumulative_compaction_num_threads_per_disk = 4
      base_compaction_num_threads_per_disk = 2
      # 降低触发 Compaction 的阈值
      cumulative_size_policy_max_consecutive_merges = 64
    3. 开启单副本导入(Single Replica Load):在多副本写入时,开启此功能可以只在一个 BE 上写入并构建 Index,然后同步到其他副本,极大减轻集群 CPU 和 IO 压力。

2. 现象:Unique Model 实时更新慢,查询延迟高

  • 原因分析
    • Doris 早期默认的 Unique 模型是 Merge-on-Read(读时合并)。在查询时,需要实时对比 key 并过滤历史版本,如果版本过多,查询性能会急剧下降。
  • 解决方案与调优
    1. 启用 Merge-on-Write(写时合并):在建表时开启 enable_unique_key_merge_on_write = true
      • 效果:写入时进行主键定位并标记删除,读的时候直接读取,性能提升 5-10 倍,几乎等同于 Duplicate 模型的查询性能。
    2. 合理设置分区和分桶:避免单表不分区,控制每个 Bucket(分桶)的数据量在 1GB ~ 10GB 之间。

二、 查询性能(Query Performance)相关问题

1. 现象:大表 JOIN 查询直接报错 OOM 或极慢

  • 原因分析
    • Shuffle 方式不合理:多表 JOIN 时,如果使用 Broadcast Join(广播),会将大表数据广播到所有节点,导致内存溢出。
    • 未利用 Colocate JOIN:关联字段不是分桶键,导致跨节点大量拉取数据。
  • 解决方案与调优
    1. 利用 Colocate Join:将经常需要 JOIN 的大表划分到同一个 Colocation Group 中,并使用相同的分桶键。这样数据在写入时就分布在相同的节点上,JOIN 时无需跨节点 Shuffle。
      sql
      -- 建表时指定
      PROPERTIES (
          "colocate_with" = "group_name"
      )
    2. Runtime Filter 优化:Doris 默认开启了 Runtime Filter(动态过滤),在 JOIN 时利用小表构建 Hash 表并生成过滤条件传给大表。可以通过 set runtime_filter_type = 'IN_OR_BLOOM_FILTER' 来优化过滤效果。
    3. 调大查询内存限制:如果确实需要大内存,临时调大 Session 级内存:
      sql
      SET exec_mem_limit = 16G; -- 默认通常是 2G/8G

2. 现象:高并发极速查询场景下,CPU 瞬时跑满

  • 原因分析
    • 分桶(Tablet)数量过多:很多用户喜欢把分桶数设得很大(比如几百个)。每个 Tablet 对应 BE 的一个线程,分桶过多导致单个查询占用了过多 CPU 线程,高并发下迅速榨干 CPU。
  • 解决方案与调优
    1. 优化分桶数(减少 Tablet 数)
      • 原则:单个 Tablet 数据量控制在 1GB~10GB。
      • 对于小表(几百万行),分桶数设置 1~5 个即可。
      • 1.2+ 版本后,可以开启自适应分桶(Dynamic Bucket),让 Doris 自动计算分桶数。
    2. 开启查询缓存(Query Cache)
      sql
      SET enable_sql_cache = true;
      SET enable_partition_cache = true;
      对于报表等重复查询较多的场景,能极大减轻 BE 压力。

三、 内存管理与系统稳定性(OOM/Crash)问题

1. 现象:BE 进程频繁 Crash(由于 OOM 进程被系统杀掉)

  • 原因分析
    • Doris 的内存管理比较复杂。当大查询、高频写入、后台 Compaction 同时发生时,内存水位超过 BE 限制(mem_limit),或者触发了操作系统的 OOM Killer。
  • 解决方案与调优
    1. 开启 Workload Group(资源组隔离)(Doris 2.0+ 强烈推荐):
      • 将业务划分为“大查询/离线导入”和“实时点查/线上报表”不同的资源组。
      • 限制大查询组的 CPU 和内存最大使用比例,防止单条 SQL 拖垮整机。
    2. 调整 BE 内存参数
      • mem_limit 默认是物理内存的 90%,如果机器上部署了其他服务,建议调低至 70%~80%。
    3. 优化 Linux 虚拟内存参数
      bash
      sysctl -w vm.max_map_count=2000000
      # 防止系统过早进行内存回收导致的卡顿
      sysctl -w vm.swappiness=0

2. 现象:FE 内存溢出(Java GC 停顿导致集群假死)

  • 原因分析
    • FE 保存了所有的元数据(Metadata)。如果元数据量太大(如:分区过多、表过多、Tablet 数量达到百万级),FE 节点的 JVM 内存很容易溢出。
  • 解决方案与调优
    1. 调大 FE JVM 内存
      • 修改 fe.conf 中的 JAVA_OPTS,默认 -Xmx8g,在生产环境根据元数据量建议调整到 16G64G
    2. 控制元数据规模
      • 分区合并:不要按“小时”甚至“分钟”分区,尽量按“天”分区。
      • 定期清理历史无用表和分区(使用 DROP 释放元数据)。
    3. 合理配置 FE 节点:采用 1 Master + 2 Follower 的高可用架构,读写分离。

四、 数据模型与结构设计优化(避坑指南)

在实际调优中,我们发现 70% 的性能问题来源于表结构设计不合理

1. 索引设计优化

  • 前缀索引(Prefix Index):Doris 默认会把建表语句中的前几个 Key 列作为前缀索引。
    • 调优:将查询中经常作为 WHERE 过滤条件的列放在建表语句的最前面。
  • Bloom Filter 索引
    • 调优:对高基数列(如 user_id, order_id)且经常用于等值过滤的列创建 Bloom Filter 索引,能极大地过滤掉不必要的数据块。
  • 倒排索引(Invert Index)(Doris 2.0+ 强力推荐):
    • 代替传统的 Elasticsearch。对文本字段做全文检索,或者对普通列做多条件组合查询,效率极高。

2. 冷热数据分层(Tiered Storage)

  • 问题:随着历史数据积累,全量使用 SSD 存储成本极高。
  • 解决:配置冷热数据分层策略。热数据留在本地 SSD,设置策略(例如 30 天后)自动将冷数据迁移到对象存储(S3/MinIO/HDFS),查询时仍然像一张表一样透明查询,成本降低 60% 以上。

总结:Doris 调优的“黄金法则”

  1. 写入要“攒批”:单次写入数据量越大越好,控制写入频率。
  2. 分桶要“克制”:单个 Tablet 控制在 1G-10G,严禁产生百万级碎片 Tablet。
  3. 模型要“写时合并”:Unique Key 必开 Merge-on-Write
  4. JOIN 要“本地化”:大表关联必设 Colocate Group
  5. 资源要“隔离”:2.0 版本后务必开启 Workload Group 限制坏 SQL。
00:00
00:00