Apache Doris 数据导入实践:四大导入方式与性能调优指南
Apache Doris 是一款高性能的实时分析型数据库,支持多种数据导入方式。本文将详细介绍 Doris 的三种主要数据导入方式:Broker Load、Spark Load 和 Insert Into,并深入探讨导入性能调优的关键技巧。
1. Doris 数据导入方式概述
Apache Doris 提供了多种数据导入方式,以满足不同场景下的数据加载需求。了解各种导入方式的特点和适用场景,是选择合适导入策略的第一步。
| Broker Load | 基于 Broker 进程的分布式导入,异步执行 | 大批量数据导入,TB 级数据 | 高并发、高吞吐、可暂停恢复 | 需要部署 Broker 服务,导入延迟较高 |
| Spark Load | 基于 Spark 计算引擎的导入,支持复杂转换 | 需要数据预处理或复杂转换场景 | 支持复杂数据转换,可扩展性强 | 配置复杂,依赖 Spark 环境 |
| Insert Into | 标准 SQL 插入,同步执行 | 小批量实时数据插入 | 实时性好,使用简单 | 不适合大批量数据,性能较低 |
流程图展示了 Doris 数据导入的基本流程:
#publish-mermaid-0-1790587930624{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-0-1790587930624 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-0-1790587930624 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-0-1790587930624 .error-icon{fill:#552222;}#publish-mermaid-0-1790587930624 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-0-1790587930624 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-0-1790587930624 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-0-1790587930624 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-0-1790587930624 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-0-1790587930624 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-0-1790587930624 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-0-1790587930624 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-0-1790587930624 .marker.cross{stroke:#333333;}#publish-mermaid-0-1790587930624 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#publish-mermaid-0-1790587930624 p{margin:0;}#publish-mermaid-0-1790587930624 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#publish-mermaid-0-1790587930624 .cluster-label text{fill:#333;}#publish-mermaid-0-1790587930624 .cluster-label span{color:#333;}#publish-mermaid-0-1790587930624 .cluster-label span p{background-color:transparent;}#publish-mermaid-0-1790587930624 .label text,#publish-mermaid-0-1790587930624 span{fill:#333;color:#333;}#publish-mermaid-0-1790587930624 .node rect,#publish-mermaid-0-1790587930624 .node circle,#publish-mermaid-0-1790587930624 .node ellipse,#publish-mermaid-0-1790587930624 .node polygon,#publish-mermaid-0-1790587930624 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-0-1790587930624 .rough-node .label text,#publish-mermaid-0-1790587930624 .node .label text,#publish-mermaid-0-1790587930624 .image-shape .label,#publish-mermaid-0-1790587930624 .icon-shape .label{text-anchor:middle;}#publish-mermaid-0-1790587930624 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-0-1790587930624 .rough-node .label,#publish-mermaid-0-1790587930624 .node .label,#publish-mermaid-0-1790587930624 .image-shape .label,#publish-mermaid-0-1790587930624 .icon-shape .label{text-align:center;}#publish-mermaid-0-1790587930624 .node.clickable{cursor:pointer;}#publish-mermaid-0-1790587930624 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-0-1790587930624 .arrowheadPath{fill:#333333;}#publish-mermaid-0-1790587930624 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-0-1790587930624 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-0-1790587930624 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-0-1790587930624 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-0-1790587930624 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-0-1790587930624 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-0-1790587930624 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-0-1790587930624 .cluster text{fill:#333;}#publish-mermaid-0-1790587930624 .cluster span{color:#333;}#publish-mermaid-0-1790587930624 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-0-1790587930624 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-0-1790587930624 rect.text{fill:none;stroke-width:0;}#publish-mermaid-0-1790587930624 .icon-shape,#publish-mermaid-0-1790587930624 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-0-1790587930624 .icon-shape p,#publish-mermaid-0-1790587930624 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-0-1790587930624 .icon-shape .label rect,#publish-mermaid-0-1790587930624 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-0-1790587930624 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-0-1790587930624 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-0-1790587930624 .node .neo-node{stroke:#9370DB;}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].node rect,#publish-mermaid-0-1790587930624 [data-look=\”neo\”].cluster rect,#publish-mermaid-0-1790587930624 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1790587930624 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-0-1790587930624 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
数据准备
选择导入方式
Broker Load
Spark Load
Insert Into
上传文件到存储系统
创建导入任务
Broker 处理数据
导入结果返回
Spark 读取数据
Spark 数据处理
导入到 Doris
执行 SQL 插入
实时导入结果
导入完成
2. Broker Load 实践与优化
Broker Load 是 Doris 提供的一种高效的大批量数据导入方式,基于 Broker 进程实现数据的并行加载。
2.1 基本语法与使用
Broker Load 使用以下基本语法:
LOAD LABEL database.table_label
(
DATA INFILE("hdfs://path/to/data/*")
INTO TABLE table_name
[PARTITION(p1)]
[COLUMNS(col1, col2, col3)]
[SET(col4 = col1 * 10)]
)
PROPERTIES
(
"broker.name" = "broker_name",
"broker.hadoop.default.fs" = "hdfs://namenode:8020",
"timeout_second" = "3600"
);
2.2 关键参数优化
Broker Load 的性能受多个参数影响,主要包括:
- 并发度:通过 parallel_fragment_exec_instance_num 参数控制,通常设置为 CPU 核心数的 2-3 倍
- 内存限制:通过 exec_mem_limit 参数设置,建议为总内存的 20%-30%
- 超时时间:根据数据量大小合理设置,避免任务因超时失败
2.3 常见问题与解决方案
- 导入失败:检查 Broker 服务是否正常,文件路径是否存在且可访问
- 导入速度慢:提高并发度,优化数据格式(如使用列式存储 Parquet)
- 内存不足:适当调小 exec_mem_limit 或增加 BE 节点
3. Spark Load 实践与优化
Spark Load 利用 Spark 分布式计算能力,适合处理需要复杂数据转换的大规模数据导入场景。
3.1 基本语法与配置
Spark Load 的基本语法如下:
LOAD LABEL database.table_label
(
SPARK
"spark.sql"
(
"spark.executor.memory"="4g",
"spark.executor.cores"="2",
"spark.executor.instances"="10"
)
SCRIPT
"spark_load.py"
— "hdfs://path/to/data/*"
"hdfs://output/path"
)
INTO TABLE table_name
[PARTITION(p1)]
[COLUMNS(col1, col2, col3)]
[SET(col4 = expr)];
3.2 Spark 优化要点
优化 Spark Load 性能的关键点:
- 资源配置:根据数据量调整 executor 数量和内存,通常 executor 数量设置为集群核心数的 1-2 倍
- 数据倾斜处理:对关键字段进行预分发,避免数据倾斜
- 序列化优化:使用 Kryo 序列化替代默认 Java 序列化
3.3 典型 Spark 导入脚本示例
# spark_load.py
from pyspark.sql import SparkSession
from pyspark.sql.functions import col
spark = SparkSession.builder.appName("SparkLoadExample").getOrCreate()
# 读取数据
df = spark.read.parquet("hdfs://input/path/*")
# 数据转换
df_transformed = df.withColumn("new_col", col("old_col") * 2)
# 写入 Doris
df_transformed.write.format("doris").option("doris.table.identifier", "database.table_name").option("doris.fenodes", "fe_host:9030").save()
spark.stop()
4. Insert Into 与综合性能调优
4.1 Insert Into 使用方法
Insert Into 是最直接的 SQL 数据插入方式,适合小批量实时数据插入:
— 单行插入
INSERT INTO table_name VALUES (1, "name", 100);
— 批量插入
INSERT INTO table_name VALUES
(1, "name1", 100),
(2, "name2", 200),
(3, "name3", 300);
— 从查询结果插入
INSERT INTO table_name SELECT * FROM source_table WHERE condition;
4.2 综合性能调优策略
针对不同导入场景的综合优化策略:
| TB 级批量数据 | Broker Load | 高并发度、列式存储、合理分区 |
| 复杂数据转换 | Spark Load | 资源优化、倾斜处理、并行度调整 |
| 实时小批量数据 | Insert Into | 批量提交、索引优化 |
| 混合场景组合使用 | 组合使用 | 合理任务调度、错峰导入 |
4.3 通用性能优化技巧
最小示例与注意事项
Broker Load 最小示例
— 1. 在 HDFS 上准备好数据文件
— 2. 创建目标表
CREATE TABLE sales_data (
date DATE,
product_id INT,
amount DECIMAL(10,2)
) DISTRIBUTED BY HASH(product_id) BUCKETS 10;
— 3. 执行 Broker Load
LOAD LABEL example.sales_label
(
DATA INFILE("hdfs://data/sales/*")
INTO TABLE sales_data
COLUMNS TERMINATED BY ","
)
PROPERTIES
(
"broker.name" = "broker_hdfs",
"broker.hadoop.default.fs" = "hdfs://namenode:8020",
"timeout_second" = "3000"
);
— 4. 查看导入状态
SHOW LOAD WHERE LABEL = "sales_label";
注意事项
- 确保 Broker 服务正常运行
- 大文件提前分割为小文件(建议 100M-1G)
- 避免导入期间修改表结构
- 确保 Spark 和 Doris 版本兼容
- 导入前进行小规模测试验证
- 合理设置 Spark 内存和并行度
- 避免单行提交,使用批量插入
- 高峰期限制提交频率
- 及时处理导入失败的情况