Doris 查询太慢?要不要试试这几项加速技术
Apache Doris 作为一款现代化的 MPP 分析型数据库,其查询性能一直是核心优势。本文将深入探讨 Doris 的查询加速技术体系,从执行引擎优化到缓存机制,从 Join 策略到资源管理,结合具体场景和代码示例,全面解析如何最大化 Doris 的查询性能。
一、执行引擎:Pipeline 与 PipelineX
1. 传统执行引擎的瓶颈传统火山模型(Volcano Model)中,每个操作符(Operator)通过 Next() 方法逐条拉取数据,导致 CPU 缓存利用率低、函数调用开销大:
复制
// 传统火山模型伪代码
while (row = child_op->Next()) {
processed_row = process(row);
emit(processed_row);
}1.2.3.4.5.
Pipeline 引擎通过向量化执行和批处理技术优化:
复制
-- 启用 Pipeline 引擎
set enable_pipeline_engine = true;
-- 查看执行计划是否使用 Pipeline
EXPLAIN SELECT ... FROM ...;1.2.3.4.5.
核心优势:
批处理:一次处理 1024 行数据(默认批大小)向量化:利用 SIMD 指令加速计算流水线并行:多线程并行执行不同阶段3. PipelineX 引擎增强PipelineX 在 Pipeline 基础上增加了本地shuffle优化:
复制
-- 启用 PipelineX
set enable_pipeline_x_engine = true;
-- 查看执行计划中的 LocalExchange 算子
EXPLAIN SELECT COUNT(*) FROM large_table GROUP BY region;1.2.3.4.5.
适用场景:
大数据量聚合查询需要本地预聚合的复杂查询二、查询缓存:SQL Cache
1. 缓存机制原理SQL Cache 基于查询文本和参数精确匹配:
复制
-- 启用 SQL Cache
SET enable_sql_cache = true;
-- 查看缓存命中率
SHOW VARIABLES LIKE %sql_cache%;1.2.3.4.5.
缓存命中条件:
查询文本完全一致(包括空格和大小写)参数值相同涉及表数据未变更2. 缓存优化实践复制
-- 1. 使用参数化查询提高缓存命中率
-- 低效方式(每次参数不同都生成新缓存项)
SELECT*FROM orders WHERE user_id =1001;
SELECT*FROM orders WHERE user_id =1002;
-- 高效方式(使用视图或CTE)
CREATEVIEW user_orders AS
SELECT*FROM orders WHERE user_id = ${user_id};
-- 2. 控制缓存粒度
-- 小结果集查询适合缓存
SELECTCOUNT(*) FROM orders WHEREdate=2023-10-01;
-- 大结果集查询禁用缓存
SET enable_sql_cache =false;
SELECT*FROM large_table;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.
三、Join 优化策略
1. Bucket Shuffle Join通过数据分布预定位减少网络传输:
复制
-- 创建表时指定分布键
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
...
) DISTRIBUTED BY HASH(user_id) BUCKETS 32;
CREATE TABLE users (
user_id BIGINT,
...
) DISTRIBUTED BY HASH(user_id) BUCKETS 32;
-- 查询自动使用 Bucket Shuffle Join
EXPLAIN
SELECT o.*, u.name
FROM orders o
JOIN users u ON o.user_id = u.user_id;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.
优化效果:
减少 90%+ 的网络数据传输适用于大表 Join 大表场景2. Colocation Join共址 Join 保证关联数据在同一节点:
复制
-- 创建同组(Group)的表
CREATE TABLE table_a (
k1 INT,
...
) DISTRIBUTED BY HASH(k1) BUCKETS 32
PROPERTIES (
"colocation_with" = "group1"
);
CREATE TABLE table_b (
k1 INT,
...
) DISTRIBUTED BY HASH(k1) BUCKETS 32
PROPERTIES (
"colocation_with" = "group1"
);
-- 查询自动使用 Colocation Join
EXPLAIN
SELECT a.*, b.*
FROM table_a a
JOIN table_b b ON a.k1 = b.k1;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.
适用场景:
频繁 Join 的维度表数据量相对稳定的表3. Runtime Filter动态过滤减少 Join 数据量:
复制
-- 启用 Runtime Filter
SET enable_runtime_filter = true;
-- 查看执行计划中的 Runtime Filter
EXPLAIN
SELECT o.*
FROM orders o
JOIN (SELECT user_id FROM vip_users) v
ON o.user_id = v.user_id;1.2.3.4.5.6.7.8.9.
优化类型:
IN Filter:适用于高基数字段Bloom Filter:适用于超高基数字段MinMax Filter:适用于数值类型复制
-- 手动调整 Runtime Filter 类型
SET runtime_filter_type = "BLOOM_FILTER";1.2.
四、高效去重技术
1. BITMAP 精准去重复制
-- 创建表时使用 BITMAP 类型
CREATE TABLE user_actions (
user_id BIGINT,
action_date DATE,
actions BITMAP BITMAP_UNION
) AGGREGATE KEY(user_id, action_date);
-- 数据导入
INSERT INTO user_actions VALUES
(1001, 2023-10-01, to_bitmap(101)),
(1001, 2023-10-01, to_bitmap(102)),
(1001, 2023-10-01, to_bitmap(101));
-- 查询去重结果
SELECT
user_id,
action_date,
bitmap_union_count(actions) AS unique_actions
FROM user_actions
GROUPBY user_id, action_date;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.
性能对比:
复制
-- 传统 COUNT(DISTINCT) 方式
SELECT user_id, COUNT(DISTINCT action_id)
FROM user_actions_raw
GROUP BY user_id;
-- BITMAP 方式(快 5-10 倍)
SELECT user_id, bitmap_union_count(actions)
FROM user_actions
GROUP BY user_id;1.2.3.4.5.6.7.8.9.
复制
-- 创建表时使用 HLL 类型
CREATE TABLE page_views (
page_id INT,
view_date DATE,
users HLL HLL_UNION
) AGGREGATE KEY(page_id, view_date);
-- 数据导入
INSERT INTO page_views VALUES
(1001, 2023-10-01, hll_hash(1001)),
(1001, 2023-10-01, hll_hash(1002)),
(1001, 2023-10-01, hll_hash(1001));
-- 查询近似去重结果
SELECT
page_id,
view_date,
hll_union_agg(users) AS approx_unique_users
FROM page_views
GROUPBY page_id, view_date;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.
误差控制:
复制
-- 设置 HLL 精度(默认 12,精度越高误差越小)
SET hll_precision = 16;1.2.
五、高并发点查优化
1. 行存与列存结合复制
-- 创建 Unique 模型表(自动行存)
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10,2),
status VARCHAR(20)
) UNIQUE KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 32;
-- 查询自动使用行存优化
SELECT * FROM orders WHERE order_id = 1000001;1.2.3.4.5.6.7.8.9.10.11.
复制
// Java 代码示例
String sql = "SELECT * FROM orders WHERE order_id = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setInt(1, 1000001);
ResultSet rs = stmt.executeQuery();
// 处理结果
}1.2.3.4.5.6.7.
优化效果:
减少 SQL 解析开销提高执行计划复用率3. 行缓存配置复制
-- 启用行缓存
SET enable_row_cache = true;
-- 调整行缓存大小(默认 2MB)
SET row_cache_mem_limit = "50MB";
-- 查看缓存命中率
SHOW BACKENDS;
-- 然后访问 BE 的 metrics 页面查看 row_cache_hit_rate1.2.3.4.5.6.7.8.9.
六、TOPN 查询优化
1. 优化原理TOPN 查询通过提前终止和堆排序优化:
复制
-- 传统方式(全排序后取前N)
SELECT * FROM orders ORDER BY amount DESC LIMIT 10;
-- 优化方式(使用 TopN 算子)
EXPLAIN
SELECT * FROM orders ORDER BY amount DESC LIMIT 10;1.2.3.4.5.6.
复制
-- 启用 TopN 优化
SET enable_topn_opt = true;
-- 调整 TopN 缓存大小
SET topn_cache_size = 1024;1.2.3.4.5.
七、查询分析与调优
1. 获取执行 Profile复制
-- 执行查询并获取 Profile
SET show_profile = true;
SELECT COUNT(*) FROM large_table GROUP BY region;
-- 查看详细 Profile
SHOW PROFILE;1.2.3.4.5.6.
关键指标分析:
ScanNode:数据扫描时间HashJoinNode:Join 阶段耗时AggregationNode:聚合计算时间ExchangeNode:数据传输时间2. 查询分析示例复制
-- 示例:分析慢查询
-- 1. 获取查询ID
SHOW PROCESSLIST;
-- 2. 获取指定查询的 Profile
SHOW PROFILE FOR QUERY query_id;
-- 3. 分析 Profile 中的瓶颈
-- 例如发现 ScanNode 耗时过长:
ScanNode:
- RowsRead: 100000000
- BytesRead: 2GB
- TotalTime: 5.2s
- IOTime: 4.8s # IO 瓶颈
-- 优化方案:增加缓存或优化存储1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.
八、资源管理与隔离
1. Workload Group 资源隔离复制
-- 创建资源组
CREATE WORKLOAD GROUP "analytics"
PROPERTIES (
"cpu_limit" = "50%",
"memory_limit" = "30%",
"concurrency_limit" = "10"
);
-- 创建资源组
CREATE WORKLOAD GROUP "etl"
PROPERTIES (
"cpu_limit" = "30%",
"memory_limit" = "50%",
"concurrency_limit" = "5"
);
-- 将用户绑定到资源组
CREATEUSERanalyst@% IDENTIFIED BYpassword
PROPERTIES ("workload_group" = "analytics");
CREATEUSERetl_user@% IDENTIFIED BYpassword
PROPERTIES ("workload_group" = "etl");1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.
复制
-- 设置查询优先级
SET QUERY_PRIORITY = HIGH;
-- 在资源组中配置优先级
ALTER WORKLOAD GROUP "analytics"
SET PROPERTIES ("query_priority" = "HIGH");1.2.3.4.5.6.
九、综合优化案例
1. 电商订单分析场景复制
-- 1. 表设计优化
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
order_date DATE,
amount DECIMAL(10,2),
status VARCHAR(20),
region VARCHAR(20),
INDEX idx_user (user_id) USING BITMAP,
INDEX idx_date (order_date)
) DUPLICATE KEY(order_id)
PARTITIONBYRANGE(order_date) (
PARTITION p202301 VALUES LESS THAN (2023-02-01),
PARTITION p202302 VALUES LESS THAN (2023-03-01)
)
DISTRIBUTED BY HASH(order_id) BUCKETS 32;
-- 2. 查询优化示例
-- 查询:各区域月度订单统计
SELECT
region,
DATE_TRUNC(month, order_date) ASmonth,
COUNT(DISTINCT order_id) AS order_count,
SUM(amount) AS total_amount
FROM orders
WHERE order_date BETWEEN2023-01-01AND2023-12-31
GROUPBY region, DATE_TRUNC(month, order_date)
ORDERBY region, month;
-- 优化措施:
-- a. 分区裁剪(自动)
-- b. 使用 BITMAP 索引加速 COUNT(DISTINCT)
-- c. 聚合下推(自动)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.
复制
-- 1. 创建物化视图
CREATE MATERIALIZED VIEW mv_user_stats
REFRESH ASYNC EVERY1 HOUR
ASSELECT
user_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount,
MAX(order_date) AS last_order_date
FROM orders
GROUPBY user_id;
-- 2. 查询物化视图
SELECT*FROM mv_user_stats
WHERE user_id =1001;
-- 3. 监控物化视图状态
SHOW MATERIALIZED VIEWS;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.
十、优秀实践总结
(1) 执行引擎选择:
通用查询启用 enable_pipeline_engine = true复杂聚合查询启用 enable_pipeline_x_engine = true(2) Join 优化策略:
优先使用 Bucket Shuffle Join频繁 Join 的表使用 Colocation Join大表 Join 小表使用 Runtime Filter(3) 去重技术选择:
精准去重使用 BITMAP大数据集近似去重使用 HLL(4) 点查优化组合:
Unique 模型 + PreparedStatement + 行缓存(5) 资源管理:
关键业务使用高优先级 Workload Group监控内存使用,避免 OOM(6) 查询分析:
定期分析慢查询 Profile关注 IO、CPU 和内存瓶颈通过综合运用这些优化技术,Apache Doris 可以在大多数分析场景下实现亚秒级响应,满足高并发、低延迟的查询需求。实际应用中,建议结合业务特点和数据特征,选择最适合的优化组合方案。
阅读剩余
THE END