返回博客

Apache Doris 深度实践(上):OLAP 引擎核心特性与调优

在 coomia-dip 的数据分析层(Data Layer)中,我们需要一个能够同时满足以下需求的 OLAP 引擎:

Coomia发布于 2025年11月9日20 分钟阅读
分享本文Twitter / X

系列:S8 技术组件深潜 · 第 1 篇 | 难度:高级 | 阅读时间:20 分钟

Apache Doris 深度实践(上):OLAP 引擎核心特性与调优

#TL;DR

  • Apache Doris 是 coomia-dip 数据分析层的核心 OLAP 引擎,采用 MPP 架构实现亚秒级交互式查询,替代了传统 ClickHouse + Elasticsearch 的双引擎方案
  • 通过合理的数据模型选择(Duplicate/Aggregate/Unique)、分区分桶策略、以及物化视图预聚合,可在十亿级数据上实现 P99 < 2s 的查询延迟
  • 本文深入剖析 Doris 在 coomia-dip 中的部署架构、表模型设计、查询调优、以及与 Iceberg 外表的集成实践

#1. 为什么选择 Apache Doris

#1.1 OLAP 引擎选型背景

在 coomia-dip 的数据分析层(Data Layer)中,我们需要一个能够同时满足以下需求的 OLAP 引擎:

  • 亚秒级交互式查询:仪表盘和报表场景要求低延迟
  • 高并发查询:多租户 SaaS 模式下并发查询数可达数百
  • 实时数据摄入:支持从 Kafka 实时导入流式数据
  • 标准 SQL 兼容:降低用户学习成本
  • 联邦查询能力:能够查询 Iceberg、Hive 等外部数据源

#1.2 候选方案对比

我们评估了以下 OLAP 引擎:

特性Apache DorisClickHouseApache DruidStarRocks
架构模型MPP (FE/BE)Shared-NothingLambdaMPP (FE/BE)
SQL 兼容性MySQL 协议自有协议有限 SQLMySQL 协议
实时导入Routine LoadKafka EngineKafka IndexingRoutine Load
联邦查询Multi-Catalog有限Multi-Catalog
Join 性能优秀一般优秀
运维复杂度
社区活跃度高 (Apache TLP)
向量索引支持 (2.1+)不支持不支持不支持

最终选择 Doris 的核心原因

  1. 统一引擎:Doris 2.1+ 同时支持 OLAP 分析、向量检索和全文检索,避免了维护多套引擎的运维负担
  2. MySQL 兼容:业务团队可以使用熟悉的 MySQL 客户端和 JDBC 驱动直接连接
  3. Multi-Catalog 联邦查询:可以直接查询 Iceberg 表,与我们的 Lakehouse 架构无缝集成
  4. 云原生友好:存算分离架构支持弹性扩缩容

#1.3 Doris 在 coomia-dip 架构中的位置

Code
┌─────────────────────────────────────────────────┐
│                   Control Layer (Control)              │
│              Spring Boot 3.x + gRPC              │
└──────────────┬──────────────────┬────────────────┘
               │ gRPC             │ gRPC
    ┌──────────▼──────────┐  ┌───▼────────────────┐
    │   Data Layer (Data)     │  │  Reasoning & Decision Layer (Reasoning)│
    │   Quarkus 3.x        │  │  Python + FastAPI   │
    │   ┌──────────────┐   │  └────────────────────┘
    │   │ Apache Doris  │   │
    │   │ (OLAP Engine) │   │
    │   └──────┬───────┘   │
    │          │            │
    │   ┌──────▼───────┐   │
    │   │ Apache Iceberg│   │
    │   │ + Nessie      │   │
    │   └──────────────┘   │
    └──────────────────────┘

Doris 在平台中承担三个核心角色:

  • 分析查询引擎:为 Ontology 对象提供聚合分析能力
  • 实时数据服务层:接收 Flink CDC 实时写入的变更数据
  • 联邦查询网关:通过 Multi-Catalog 查询 Iceberg 冷数据

#2. 部署架构与集群规划

#2.1 coomia-dip 中的 Doris 部署拓扑

coomia-dip 采用存算一体的经典部署模式(适用于中小规模场景),同时预留了向存算分离演进的路径。

YAML
# deployment-Layer/docker-compose/doris-cluster.yml
version: '3.8'
services:
  doris-fe-1:
    image: apache/doris:2.1.4-fe
    hostname: doris-fe-1
    environment:
      - FE_SERVERS=doris-fe-1:9010,doris-fe-2:9010,doris-fe-3:9010
      - FE_ID=1
    ports:
      - "8030:8030"   # HTTP API
      - "9030:9030"   # MySQL Protocol
      - "9010:9010"   # Edit Log Port
    volumes:
      - doris-fe-1-meta:/opt/apache-doris/fe/doris-meta
    deploy:
      resources:
        limits:
          memory: 8G
          cpus: '4'
    networks:
      - coomia-dip-net

  doris-fe-2:
    image: apache/doris:2.1.4-fe
    hostname: doris-fe-2
    environment:
      - FE_SERVERS=doris-fe-1:9010,doris-fe-2:9010,doris-fe-3:9010
      - FE_ID=2
    volumes:
      - doris-fe-2-meta:/opt/apache-doris/fe/doris-meta
    deploy:
      resources:
        limits:
          memory: 8G
          cpus: '4'
    networks:
      - coomia-dip-net

  doris-fe-3:
    image: apache/doris:2.1.4-fe
    hostname: doris-fe-3
    environment:
      - FE_SERVERS=doris-fe-1:9010,doris-fe-2:9010,doris-fe-3:9010
      - FE_ID=3
    volumes:
      - doris-fe-3-meta:/opt/apache-doris/fe/doris-meta
    deploy:
      resources:
        limits:
          memory: 8G
          cpus: '4'
    networks:
      - coomia-dip-net

  doris-be-1:
    image: apache/doris:2.1.4-be
    hostname: doris-be-1
    environment:
      - FE_SERVERS=doris-fe-1:9010,doris-fe-2:9010,doris-fe-3:9010
      - BE_ADDR=doris-be-1:9050
    volumes:
      - doris-be-1-data:/opt/apache-doris/be/storage
    deploy:
      resources:
        limits:
          memory: 32G
          cpus: '16'
    networks:
      - coomia-dip-net

  doris-be-2:
    image: apache/doris:2.1.4-be
    hostname: doris-be-2
    environment:
      - FE_SERVERS=doris-fe-1:9010,doris-fe-2:9010,doris-fe-3:9010
      - BE_ADDR=doris-be-2:9050
    volumes:
      - doris-be-2-data:/opt/apache-doris/be/storage
    deploy:
      resources:
        limits:
          memory: 32G
          cpus: '16'
    networks:
      - coomia-dip-net

  doris-be-3:
    image: apache/doris:2.1.4-be
    hostname: doris-be-3
    environment:
      - FE_SERVERS=doris-fe-1:9010,doris-fe-2:9010,doris-fe-3:9010
      - BE_ADDR=doris-be-3:9050
    volumes:
      - doris-be-3-data:/opt/apache-doris/be/storage
    deploy:
      resources:
        limits:
          memory: 32G
          cpus: '16'
    networks:
      - coomia-dip-net

volumes:
  doris-fe-1-meta:
  doris-fe-2-meta:
  doris-fe-3-meta:
  doris-be-1-data:
  doris-be-2-data:
  doris-be-3-data:

networks:
  coomia-dip-net:
    external: true

#2.2 集群规模规划指南

数据规模FE 节点BE 节点BE 内存BE CPU存储
< 1TB1 (Follower)316GB8核500GB SSD
1-10TB3 (1 Leader + 2 Follower)5-1032GB16核2TB SSD
10-50TB3 FE + 2 Observer10-2064GB32核4TB NVMe
> 50TB5 FE + Observer20+128GB64核8TB NVMe RAID

#2.3 关键配置参数

FE 配置 (fe.conf)

PROPERTIES
# 元数据目录
meta_dir = /opt/apache-doris/fe/doris-meta

# JVM 堆内存(建议物理内存的 50-70%)
JAVA_OPTS="-Xmx6g -Xms6g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"

# 查询超时(秒)
query_timeout = 300

# 最大连接数
qe_max_connection = 4096

# 并行执行实例数
parallel_fragment_exec_instance_num = 8

# 开启 Pipeline 执行引擎
enable_pipeline_engine = true

# Catalog 相关
enable_multi_catalog = true

# 审计日志
audit_log_dir = /opt/apache-doris/fe/log
audit_log_roll_num = 90

BE 配置 (be.conf)

PROPERTIES
# 存储目录(多盘配置提高 IO 吞吐)
storage_root_path = /data1;/data2;/data3

# 内存限制(建议物理内存的 80%)
mem_limit = 80%

# Compaction 线程数
compaction_task_num_per_disk = 4

# 向量化执行引擎
enable_vectorized_engine = true

# Page Cache
disable_storage_page_cache = false
storage_page_cache_limit = 20%

# Chunk 大小(影响查询内存使用)
chunk_reserved_bytes_limit = 2147483648

# 网络缓冲区
brpc_num_threads = 256
thrift_server_max_worker_threads = 4096

#3. 数据模型深度解析

#3.1 三种数据模型对比

Doris 提供三种表模型,选择正确的模型对查询性能至关重要:

Duplicate 模型(明细模型)

保留所有原始数据,不做任何聚合。适用于需要保留完整明细的场景。

SQL
-- coomia-dip 中的 Ontology 操作审计日志表
CREATE TABLE ontology_audit_log (
    event_time     DATETIME       NOT NULL COMMENT '事件时间',
    tenant_id      VARCHAR(64)    NOT NULL COMMENT '租户ID',
    user_id        VARCHAR(64)    NOT NULL COMMENT '用户ID',
    object_type    VARCHAR(128)   NOT NULL COMMENT '对象类型',
    object_rid     VARCHAR(256)   NOT NULL COMMENT '对象RID',
    action         VARCHAR(32)    NOT NULL COMMENT '操作类型',
    property_name  VARCHAR(128)   COMMENT '属性名',
    old_value      TEXT           COMMENT '旧值',
    new_value      TEXT           COMMENT '新值',
    source_ip      VARCHAR(45)    COMMENT '来源IP',
    request_id     VARCHAR(64)    COMMENT '请求ID',
    duration_ms    INT            COMMENT '处理耗时(ms)'
)
DUPLICATE KEY(event_time, tenant_id, user_id)
PARTITION BY RANGE(event_time) (
    FROM ("2024-01-01") TO ("2026-12-31") INTERVAL 1 MONTH
)
DISTRIBUTED BY HASH(tenant_id) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "MONTH",
    "dynamic_partition.start" = "-12",
    "dynamic_partition.end" = "3",
    "dynamic_partition.prefix" = "p",
    "dynamic_partition.buckets" = "16",
    "compaction_policy" = "time_series"
);

Aggregate 模型(聚合模型)

相同 Key 的行在导入时自动聚合。适用于指标统计场景。

SQL
-- coomia-dip 中的对象属性变更统计表
CREATE TABLE ontology_property_stats (
    stat_date      DATE           NOT NULL COMMENT '统计日期',
    tenant_id      VARCHAR(64)    NOT NULL COMMENT '租户ID',
    object_type    VARCHAR(128)   NOT NULL COMMENT '对象类型',
    property_name  VARCHAR(128)   NOT NULL COMMENT '属性名',
    change_count   BIGINT         SUM      COMMENT '变更次数',
    unique_objects HLL            HLL_UNION COMMENT '去重对象数',
    last_changed   DATETIME       MAX      COMMENT '最后变更时间',
    avg_duration   DOUBLE         AVG      COMMENT '平均处理耗时'
)
AGGREGATE KEY(stat_date, tenant_id, object_type, property_name)
PARTITION BY RANGE(stat_date) (
    FROM ("2024-01-01") TO ("2026-12-31") INTERVAL 1 MONTH
)
DISTRIBUTED BY HASH(tenant_id) BUCKETS 8
PROPERTIES (
    "replication_num" = "3"
);

Unique 模型(唯一键模型)

相同 Key 的行自动覆盖更新。适用于需要更新的维度表场景。

SQL
-- coomia-dip 中的 Ontology 对象当前状态表
CREATE TABLE ontology_object_current (
    tenant_id      VARCHAR(64)    NOT NULL COMMENT '租户ID',
    object_type    VARCHAR(128)   NOT NULL COMMENT '对象类型',
    object_rid     VARCHAR(256)   NOT NULL COMMENT '对象RID',
    display_name   VARCHAR(512)   COMMENT '显示名称',
    status         VARCHAR(32)    COMMENT '状态',
    properties     JSON           COMMENT '属性JSON',
    created_at     DATETIME       COMMENT '创建时间',
    updated_at     DATETIME       COMMENT '更新时间',
    version        BIGINT         COMMENT '版本号'
)
UNIQUE KEY(tenant_id, object_type, object_rid)
DISTRIBUTED BY HASH(tenant_id) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "enable_unique_key_merge_on_write" = "true",
    "store_row_column" = "true"
);

#3.2 模型选择决策树

Code
需要保留所有明细?
├─ 是 → Duplicate 模型
│      (审计日志、事件流、行为记录)
└─ 否 → 数据有唯一业务键?
         ├─ 是 → 需要部分列更新?
         │       ├─ 是 → Unique 模型 (Merge-on-Write)
         │       │      (维度表、状态表、实时更新表)
         │       └─ 否 → 只需预聚合指标?
         │               ├─ 是 → Aggregate 模型
         │               │      (指标统计、计数器、HLL 去重)
         │               └─ 否 → Unique 模型
         └─ 否 → Duplicate 模型

#4. 分区分桶策略

#4.1 分区策略

分区是 Doris 的第一级数据管理粒度,coomia-dip 中按数据特征采用不同分区策略:

时间范围分区(最常用)

SQL
-- 按月分区,适用于审计日志、指标数据
PARTITION BY RANGE(event_date) (
    PARTITION p202401 VALUES [('2024-01-01'), ('2024-02-01')),
    PARTITION p202402 VALUES [('2024-02-01'), ('2024-03-01')),
    -- 使用动态分区自动管理
)

动态分区配置

SQL
PROPERTIES (
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "DAY",
    "dynamic_partition.start" = "-30",    -- 保留30天历史
    "dynamic_partition.end" = "3",        -- 预创建3天分区
    "dynamic_partition.prefix" = "p",
    "dynamic_partition.buckets" = "16",
    "dynamic_partition.create_history_partition" = "true"
);

List 分区(多租户隔离)

SQL
-- 按租户分区,实现物理隔离
PARTITION BY LIST(tenant_id) (
    PARTITION p_tenant_a VALUES IN ("tenant-001"),
    PARTITION p_tenant_b VALUES IN ("tenant-002"),
    PARTITION p_tenant_c VALUES IN ("tenant-003")
)

#4.2 分桶策略

分桶是 Doris 的第二级数据分布粒度,直接影响查询并行度:

SQL
-- Hash 分桶:适用于点查和 Join 场景
DISTRIBUTED BY HASH(tenant_id, object_rid) BUCKETS 16

-- Random 分桶:适用于无明显分布键的场景
DISTRIBUTED BY RANDOM BUCKETS 32

分桶数计算公式

Code
推荐桶数 = max(
    BE节点数 * CPU核数 / 2,
    单分区数据量(GB) * 1024 / 256   -- 每桶约 256MB
)

#4.3 coomia-dip 中的分区分桶最佳实践

数据类型分区方式分桶键桶数
审计日志按天动态分区tenant_id16
指标统计按月范围分区tenant_id + metric_name8
对象状态无分区tenant_id + object_type32
关系数据按天动态分区source_rid16
决策结果按月范围分区tenant_id + decision_type8

#5. 查询优化实战

#5.1 执行计划分析

使用 EXPLAIN 分析查询计划是调优的第一步:

SQL
EXPLAIN VERBOSE
SELECT
    t.object_type,
    COUNT(*) as total_changes,
    COUNT(DISTINCT object_rid) as unique_objects,
    AVG(duration_ms) as avg_duration
FROM ontology_audit_log t
WHERE t.tenant_id = 'tenant-001'
  AND t.event_time >= '2025-01-01'
  AND t.event_time < '2025-02-01'
GROUP BY t.object_type
ORDER BY total_changes DESC
LIMIT 20;

关键关注点

  • Scan 节点:检查是否裁剪了正确的分区和桶
  • Exchange 节点:检查数据 Shuffle 策略
  • Agg 节点:检查聚合是否下推到 Scan 阶段
  • Runtime Filter:检查是否生成了运行时过滤

#5.2 物化视图加速

物化视图是 Doris 最重要的加速手段之一:

SQL
-- 为审计日志创建按对象类型的聚合物化视图
CREATE MATERIALIZED VIEW mv_audit_by_object_type AS
SELECT
    tenant_id,
    DATE_TRUNC('day', event_time) as event_date,
    object_type,
    action,
    COUNT(*) as action_count,
    COUNT(DISTINCT user_id) as unique_users,
    AVG(duration_ms) as avg_duration
FROM ontology_audit_log
GROUP BY tenant_id, DATE_TRUNC('day', event_time), object_type, action;

-- 查询时 Doris 自动路由到物化视图
SELECT object_type, SUM(action_count)
FROM ontology_audit_log
WHERE tenant_id = 'tenant-001'
GROUP BY object_type;
-- [自动命中 mv_audit_by_object_type]

#5.3 Colocation Join 优化

对于频繁 Join 的表,使用 Colocation Group 避免数据 Shuffle:

SQL
-- 将经常 Join 的表放入同一个 Colocation Group
CREATE TABLE ontology_objects (
    tenant_id      VARCHAR(64)   NOT NULL,
    object_rid     VARCHAR(256)  NOT NULL,
    object_type    VARCHAR(128)  NOT NULL,
    display_name   VARCHAR(512),
    created_at     DATETIME
)
UNIQUE KEY(tenant_id, object_rid)
DISTRIBUTED BY HASH(tenant_id, object_rid) BUCKETS 16
PROPERTIES (
    "colocate_with" = "ontology_group"
);

CREATE TABLE ontology_relations (
    tenant_id      VARCHAR(64)   NOT NULL,
    source_rid     VARCHAR(256)  NOT NULL,
    target_rid     VARCHAR(256)  NOT NULL,
    relation_type  VARCHAR(128)  NOT NULL,
    created_at     DATETIME
)
DUPLICATE KEY(tenant_id, source_rid, target_rid)
DISTRIBUTED BY HASH(tenant_id, source_rid) BUCKETS 16
PROPERTIES (
    "colocate_with" = "ontology_group"
);

#5.4 Runtime Filter 调优

Runtime Filter 可以在 Join 时提前过滤数据,大幅减少扫描量:

SQL
-- 全局 Session 变量设置
SET runtime_filter_mode = "GLOBAL";
SET runtime_filter_type = "IN_OR_BLOOM_FILTER,MIN_MAX";
SET runtime_filter_max_in_num = 4096;
SET runtime_filter_wait_time_ms = 2000;

#5.5 查询性能基准测试

我们在 coomia-dip 的测试环境(3 BE 节点,各 32GB/16核)上进行了基准测试:

查询场景数据量未优化延迟优化后延迟优化手段
单租户审计日志聚合5亿行3.2s0.4s物化视图 + 分区裁剪
跨表 Join (对象+关系)1亿行8.5s1.2sColocation Join
多维指标查询10亿行12.1s1.8s物化视图 + Runtime Filter
模糊搜索5000万行5.6s0.3s倒排索引(见第2篇)
向量相似度搜索1000万向量2.1s0.15s向量索引(见第2篇)
点查(单条记录)10亿行0.5s0.02s行存 + 短路径

#6. 实时数据导入

#6.1 Routine Load(Kafka 消费)

coomia-dip 使用 Routine Load 从 Kafka 实时消费 Ontology 变更事件:

SQL
CREATE ROUTINE LOAD ontology_db.load_audit_events
ON ontology_audit_log
COLUMNS(
    event_time, tenant_id, user_id, object_type,
    object_rid, action, property_name,
    old_value, new_value, source_ip,
    request_id, duration_ms
),
COLUMNS TERMINATED BY "|"
PROPERTIES (
    "desired_concurrent_number" = "3",
    "max_batch_interval" = "20",
    "max_batch_rows" = "200000",
    "max_batch_size" = "104857600",
    "strict_mode" = "false",
    "format" = "json",
    "jsonpaths" = "[
        \"$.event_time\", \"$.tenant_id\", \"$.user_id\",
        \"$.object_type\", \"$.object_rid\", \"$.action\",
        \"$.property_name\", \"$.old_value\", \"$.new_value\",
        \"$.source_ip\", \"$.request_id\", \"$.duration_ms\"
    ]"
)
FROM KAFKA (
    "kafka_broker_list" = "kafka-1:9092,kafka-2:9092,kafka-3:9092",
    "kafka_topic" = "ontology-audit-events",
    "kafka_partitions" = "0,1,2,3,4,5",
    "kafka_offsets" = "OFFSET_BEGINNING",
    "property.group.id" = "doris_audit_consumer",
    "property.kafka_default_offsets" = "OFFSET_END"
);

#6.2 Stream Load(批量导入)

对于 Flink 作业的批量写入,使用 Stream Load:

Python
# data-Layer/src/main/python/doris_stream_load.py
import requests
import json
from typing import List, Dict

class DorisStreamLoader:
    """Doris Stream Load 客户端封装"""

    def __init__(self, fe_host: str, port: int = 8030,
                 user: str = "root", password: str = ""):
        self.base_url = f"http://{fe_host}:{port}"
        self.auth = (user, password)

    def load_json_data(
        self,
        database: str,
        table: str,
        data: List[Dict],
        label: str = None
    ) -> Dict:
        """通过 Stream Load 导入 JSON 数据"""
        url = f"{self.base_url}/api/{database}/{table}/_stream_load"

        headers = {
            "Content-Type": "application/json",
            "Expect": "100-continue",
            "format": "json",
            "strip_outer_array": "true",
            "max_filter_ratio": "0.1"
        }

        if label:
            headers["label"] = label

        payload = json.dumps(data)

        response = requests.put(
            url,
            headers=headers,
            data=payload,
            auth=self.auth
        )

        result = response.json()
        if result.get("Status") != "Success":
            raise Exception(f"Stream Load failed: {result}")

        return result

#6.3 导入性能对比

导入方式吞吐量延迟适用场景
Stream Load100-200MB/s秒级Flink 批量写入
Routine Load50-100MB/s秒级Kafka 实时消费
Broker Load200-500MB/s分钟级HDFS/S3 大文件导入
INSERT INTO10-50MB/s秒级小批量/单条写入

#7. Multi-Catalog 联邦查询

#7.1 Iceberg Catalog 配置

coomia-dip 通过 Doris 的 Multi-Catalog 直接查询 Iceberg 表:

SQL
-- 创建 Iceberg Catalog(使用 Nessie 作为元数据后端)
CREATE CATALOG iceberg_catalog PROPERTIES (
    "type" = "iceberg",
    "iceberg.catalog.type" = "rest",
    "uri" = "http://nessie-server:19120/api/v2",
    "warehouse" = "s3://coomia-dip-lakehouse/warehouse",
    "s3.endpoint" = "http://minio:9000",
    "s3.access-key" = "${S3_ACCESS_KEY}",
    "s3.secret-key" = "${S3_SECRET_KEY}"
);

-- 跨 Catalog 查询:Doris 内部表 JOIN Iceberg 外表
SELECT
    d.tenant_id,
    d.object_type,
    d.action,
    COUNT(*) as recent_count,
    i.total_historical_count
FROM internal.ontology_db.ontology_audit_log d
JOIN iceberg_catalog.lakehouse.historical_audit_stats i
    ON d.tenant_id = i.tenant_id
    AND d.object_type = i.object_type
WHERE d.event_time >= CURRENT_DATE - INTERVAL 7 DAY
GROUP BY d.tenant_id, d.object_type, d.action, i.total_historical_count;

#7.2 查询下推优化

Doris 对 Iceberg 外表支持谓词下推和列裁剪:

SQL
-- 以下查询的 WHERE 条件会被下推到 Iceberg 层
-- 利用 Iceberg 的分区裁剪和 Min/Max 统计信息
SELECT * FROM iceberg_catalog.lakehouse.ontology_snapshots
WHERE snapshot_date = '2025-03-01'  -- 分区裁剪
  AND tenant_id = 'tenant-001'     -- 谓词下推
  AND status = 'ACTIVE';           -- 谓词下推

#8. 监控与运维

#8.1 关键监控指标

SQL
-- 查看集群状态
SHOW BACKENDS;
SHOW FRONTENDS;

-- 查看表的数据分布
SHOW DATA FROM ontology_audit_log;

-- 查看正在运行的查询
SHOW PROCESSLIST;

-- 查看 Compaction 状态
SHOW TABLET STORAGE FORMAT;

-- 查看 Routine Load 状态
SHOW ROUTINE LOAD FOR load_audit_events;

#8.2 Prometheus + Grafana 监控集成

YAML
# deployment-Layer/monitoring/prometheus/doris-targets.yml
- targets:
    - doris-fe-1:8030
    - doris-fe-2:8030
    - doris-fe-3:8030
  labels:
    component: doris-fe

- targets:
    - doris-be-1:8040
    - doris-be-2:8040
    - doris-be-3:8040
  labels:
    component: doris-be

核心监控面板指标

指标告警阈值说明
doris_be_mem_usage_percent> 85%BE 内存使用率
doris_be_disk_usage_percent> 80%磁盘使用率
doris_fe_query_latency_ms_p99> 5000P99 查询延迟
doris_be_compaction_score> 100Compaction 积压
doris_fe_connection_total> 3000活跃连接数
doris_be_stream_load_rows_rate< 10000导入速率下降

#8.3 常见运维操作

扩容 BE 节点

SQL
-- 1. 启动新 BE 节点
-- 2. 注册到 FE
ALTER SYSTEM ADD BACKEND "new-be-host:9050";

-- 3. 检查均衡状态
SHOW PROC '/cluster_balance/cluster_load_stat';

-- 4. 手动触发数据均衡(如需)
ADMIN SET FRONTEND CONFIG ("tablet_rebalancer_type" = "BeLoad");

表 Schema 变更

SQL
-- 添加列(Light Schema Change,秒级完成)
ALTER TABLE ontology_audit_log
ADD COLUMN correlation_id VARCHAR(128) COMMENT '关联ID';

-- 修改列类型
ALTER TABLE ontology_audit_log
MODIFY COLUMN old_value VARCHAR(4096);

#9. 常见陷阱与解决方案

#9.1 数据倾斜

问题:某些租户数据量远大于其他租户,导致部分桶数据膨胀。

解决方案

SQL
-- 使用复合分桶键分散数据
DISTRIBUTED BY HASH(tenant_id, object_rid) BUCKETS 32

-- 对超大租户使用单独分区
PARTITION BY LIST(tenant_id) (
    PARTITION p_big_tenant VALUES IN ("tenant-big-001"),
    PARTITION p_others VALUES IN ("tenant-002", "tenant-003", ...)
)

#9.2 Compaction 积压

问题:高频写入导致 Compaction 跟不上,影响查询性能。

解决方案

PROPERTIES
# be.conf 调整
compaction_task_num_per_disk = 8
cumulative_compaction_rounds_for_each_base_compaction_round = 15
compaction_policy = time_series  # 时序数据专用策略

#9.3 内存溢出

问题:复杂查询消耗过多内存导致 BE OOM。

解决方案

SQL
-- 设置单查询内存限制
SET exec_mem_limit = 4294967296;  -- 4GB

-- 开启溢写磁盘
SET enable_spill = true;
SET spill_storage_limit = "50GB";

-- 使用资源组隔离不同负载
CREATE WORKLOAD GROUP 'analytics' PROPERTIES (
    "cpu_share" = "20",
    "memory_limit" = "50%",
    "enable_memory_overcommit" = "true"
);

#9.4 查询超时

问题:大范围扫描查询超时。

解决方案

SQL
-- 1. 检查是否命中分区裁剪
EXPLAIN SELECT * FROM ontology_audit_log
WHERE event_time > '2025-01-01';

-- 2. 添加物化视图预计算
-- 3. 调整超时参数
SET query_timeout = 600;

-- 4. 使用异步查询(大查询场景)
-- 提交异步查询,返回 query_id
-- 后台执行,客户端轮询结果

#10. 与 Palantir Foundry 的对比

能力Palantir Foundrycoomia-dip (Doris)
OLAP 查询Foundry AnalyticsDoris MPP 查询
实时数据Pipeline BuilderRoutine Load + Flink
数据联邦Foundry DatasetsMulti-Catalog
搜索Phonograph倒排索引(见第2篇)
向量检索无原生支持向量索引(见第2篇)
物化视图衍生数据集同步/异步物化视图
数据治理Foundry GovernanceWorkload Group

coomia-dip 通过 Doris 实现了与 Foundry 对标的分析能力,且在向量检索、全文检索方面提供了 Foundry 不具备的统一引擎优势。

#Key Takeaways

  1. 模型选择是性能基石:根据业务场景选择正确的 Duplicate/Aggregate/Unique 模型,可以避免 80% 的性能问题。在 coomia-dip 中,审计日志用 Duplicate、指标统计用 Aggregate、状态数据用 Unique。

  2. 分区分桶策略决定查询上限:合理的分区(时间维度)+ 分桶(业务键)策略是实现亚秒级查询的前提。coomia-dip 中所有时序表采用动态分区管理生命周期。

  3. 物化视图是最有效的加速手段:对于固定模式的聚合查询,物化视图可以将延迟从秒级降到毫秒级,在 coomia-dip 仪表盘场景中提速 5-10 倍。

#下一篇预告

S8-02: Apache Doris 深度实践(下):向量索引+倒排索引 — 深入探讨 Doris 2.1+ 的向量检索和全文检索能力,包括 HNSW 索引构建、混合查询(OLAP + 向量 + 全文)的实现方案,以及与独立 Elasticsearch/Milvus 方案的性能对比。

Tags: #apache-doris #olap #mpp #query-optimization #data-modeling #coomia-dip #Layer-c