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.
2. Pipeline 执行引擎原理

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; -- 高效方式(使用视图或CTECREATEVIEW 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.
2. HLL 近似去重
复制
-- 创建表时使用 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.
2. PreparedStatement 优化
复制
// 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 查询通过提前终止和堆排序优化:

复制
-- 传统方式(全排序后取前NSELECT * 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.
2. 配置优化
复制
-- 启用 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.
2. 查询优先级管理
复制
-- 设置查询优先级 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.
2. 实时监控仪表盘场景
复制
-- 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