Doris 查询优化秘籍(上篇):关键优化策略剖析
在大数据开发的领域中,Apache Doris 凭借其强大的性能,成为处理海量数据的有力工具。若想让 Doris 在数据处理中发挥出最大效能,查询优化必不可少。本文作为 Doris 查询优化秘籍的上篇,将深入剖析 Schema、索引优化以及使用分区裁剪优化扫表的关键策略。
一、Schema 优化:筑牢数据处理根基
Schema 就如同大厦的蓝图,合理的 Schema 设计能够充分发挥 Doris 的特性,提升系统性能;反之,不合理的 Schema 则可能成为性能瓶颈。
1.1 表引擎抉择
Doris 支持 Duplicate、Unique、Aggregate 三种表模型,其中 Unique 又细分为 Merge-On-Read(MOR)和 Merge-On-Write(MOW)。不同的表模型适用于不同的业务场景,性能表现也有所差异。打个比方,Duplicate 表模型如同高速公路,能让查询快速通行,适用于对查询性能要求极高且无数据更新需求的场景;MOW 表模型则像城市主干道,性能较为平稳;MOR 和 Aggregate 表模型类似普通街道,虽能满足基本需求,但在性能上稍显逊色。一般来说,查询性能排序为:Duplicate > MOW > MOR == Aggregate。
优化建议:当业务追求极致的查询速度,且对数据更新没有要求时,优先选择 Duplicate 表模型,就像出行追求速度时首选高速公路一样。
⚠️在 1.2 版本后,Doris 中的主键模型默认使用写时合并模式
1.2 分桶列甄选
分桶操作在 Doris 中至关重要,合理选择分桶列能够避免数据倾斜,提升系统并行处理能力。这就好比快递分拣,将快递合理分配到不同的站点,能够提高派送效率。若分桶列选择不当,就会导致数据像所有快递都集中到一个站点一样,出现数据倾斜。
例如,创建 t1 表时:
CREATE TABLE `t1` (
`c1` INT NULL,
`c2` INT NULL
) ENGINE=OLAP
DUPLICATE KEY(`c1`)
DISTRIBUTED BY HASH(`c2`) BUCKETS 64
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
若 c2 列的值在数据导入时全部为 null,即便设置了 64 个分桶,也会出现严重的数据倾斜。此时,将分桶列改为 c1:
CREATE TABLE `t1` (
`c1` INT NULL,
`c2` INT NULL
) ENGINE=OLAP
DUPLICATE KEY(`c1`)
DISTRIBUTED BY HASH(`c1`) BUCKETS 64
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
就能实现数据的均匀分布,提升系统性能。
优化建议:定期检查分桶列是否存在数据倾斜问题,可通过select 分桶列,count(*) from table group by 分桶列 order by count(*) desc limit 100;
语句进行确认。若存在数据倾斜,应更换为在业务含义上具有充分散列特性的字段作为分桶列。
1.3 Key 列设定
在三种表模型中,Doris 会依据建表 Schema 中指定的 Duplicate Key、Unique Key 或 Aggregate Key,在存储层面将数据按 Key 列排序。这就如同图书馆按照书籍类别摆放书籍,方便读者快速查找。利用这一特性,将业务查询中频繁使用的等值或范围查询列定义为 Key 列,能显著提升查询速度。
例如,对于频繁使用 c1 列进行查询的业务:
select * from t1 where t1.c1 = 1;
可在建表时将 c1 列设为 Key 列:
CREATE TABLE `t1` (
`c1` INT NULL,
`c2` INT NULL
) ENGINE=OLAP
DUPLICATE KEY(`c1`)
DISTRIBUTED BY HASH(`c2`) BUCKETS 10
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
优化建议:将业务查询中频繁使用的列设定为 Key 列,为数据查询提供便捷的 “索引标签”。
1.4 字段类型优化
在数据库系统中,不同类型的数据处理复杂度不同。变长类型和高精类型的数据处理相对复杂,而定长类型和低精类型的数据处理更为高效。这就好比使用复杂工具和简单工具完成任务,简单工具往往更高效。因此,在满足业务需求的前提下,应优先选择定长类型和低精类型,避免使用变长类型和高精类型。
例如,使用 BIGINT 替代 VARCHAR 或 STRING 类型的字段,用 FLOAT / INT / BIGINT 替换 DECIMAL 类型的字段:
-- 替换前
CREATE TABLE `t2` (
`id` VARCHAR(10) NULL,
`amount` DECIMAL(10, 2) NULL
);
-- 替换后
CREATE TABLE `t2` (
`id` BIGINT NULL,
`amount` FLOAT NULL
);
优化建议:在定义 Schema 类型时,遵循定长和低精优先的原则,选择更高效的 “数据处理工具”。
1.5 分区策略规划
合理的分区策略能有效减少查询时扫描的数据量,就像在大型仓库中按类别分区存放货物,找货时能快速定位。Doris 支持按时间、范围等多种方式分区。例如,对于按时间存储的业务数据,按天或按月分区,能在查询特定时间段数据时,仅扫描相关分区,大大提高查询效率。
CREATE TABLE sales (
sale_date DATE,
product_id INT,
amount DECIMAL(10, 2)
)
PARTITION BY RANGE(sale_date) (
PARTITION p202301 VALUES LESS THAN ('2023-02-01'),
PARTITION p202302 VALUES LESS THAN ('2023-03-01')
)
DISTRIBUTED BY HASH(product_id) BUCKETS 16
PROPERTIES
(
"replication_num" = "1"
);
优化建议:根据业务数据的特点和查询需求,选择合适的分区方式和分区粒度。如果数据量增长快,可适当增加分区数量;若查询频繁涉及多个分区,可考虑调整分区范围。
1.6 数据压缩策略
数据压缩可以有效减少数据存储量,降低 I/O 开销,就像把衣物压缩进真空袋,节省空间且便于搬运。Doris 支持多种压缩算法,如 LZ4、Zlib 等。一般来说,LZ4 压缩速度快,适合对实时性要求较高的场景;Zlib 压缩比高,能节省更多存储空间。
CREATE TABLE logs (
log_time TIMESTAMP,
log_message VARCHAR(255)
)
DUPLICATE KEY(log_time)
DISTRIBUTED BY HASH(log_time) BUCKETS 8
PROPERTIES (
"replication_num" = "1",
"storage_format" = "V2",
"compresion" = "LZ4"
);
优化建议:根据数据的读写频率和存储需求,选择合适的压缩算法。对于写入频繁且对查询实时性要求高的数据,优先选择 LZ4;对于历史数据或查询频率较低的数据,可考虑使用 Zlib 以节省存储空间。
二、索引优化:打造高效数据检索工具
索引在 Doris 查询优化中起着关键作用,合理的索引设计能够大幅提升查询效率,就像精准的导航地图,帮助我们在海量数据中快速找到目标。
2.1 前缀索引运用
Doris 内置前缀索引功能,建表时会自动取表 Key 的前 36 字节作为前缀索引。这就像是地图的目录,通过前缀索引,我们可以快速定位到相关的数据。
例如,创建 t1 表时:
CREATE TABLE `t1` (
`c1` VARCHAR(10) NULL,
`c2` VARCHAR(10) NULL
) ENGINE=OLAP
DUPLICATE KEY(`c1`)
DISTRIBUTED BY HASH(`c2`) BUCKETS 10
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
若查询使用 c2 字段进行过滤:
select * from t1 where t1.c2 = '1';
由于 c1 在前,c2 在后,此时无法利用前缀索引的加速功能。调整列顺序后:
CREATE TABLE `t1` (
`c2` VARCHAR(10) NULL,
`c1` VARCHAR(10) NULL
) ENGINE=OLAP
DUPLICATE KEY(`c2`)
DISTRIBUTED BY HASH(`c1`) BUCKETS 10
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
即可利用前缀索引加速查询。
优化提示:在定义 schema 列顺序时,参考业务查询过滤中的高频高优列,以充分利用 Doris 的前缀索引加速功能。
⚠️当遇到 VARCHAR 类型时,前缀索引会直接截断。如果第一列即为 VARCHAR,那么即使没有达到 36 字节,也会直接截断,后面的列不再加入前缀索引。
2.2 倒排索引应用
Doris 支持倒排索引作为二级索引,用于加速等值、范围及文本类型的全文检索等业务场景。倒排索引就像是一个特殊的搜索引擎,它的创建和管理独立,不影响原始表 Schema 和无需重新导入表数据。
例如,对于文章表:
CREATE TABLE `articles` (
`id` INT,
`title` VARCHAR(100),
`content` VARCHAR(1000)
) ENGINE=OLAP
DUPLICATE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 10;
创建倒排索引:
CREATE INDEX idx_content ON articles(content);
当进行关键词搜索时,倒排索引能快速定位到包含关键词的文章,提高查询效率。
优化建议:对于文本类型的全文检索,以及字符串、数值、日期时间类型字段上的等值或范围查询,均可利用倒排索引来加速查询。
三、使用分区裁剪优化扫表:精准筛选数据
分区裁剪是一种智能的数据筛选机制,就像在一个巨大的书架上找书,它能根据查询条件,精准定位到存放目标书籍的区域,而不是盲目地翻阅整个书架。当我们在 Doris 中进行查询时,分区裁剪功能会分析查询条件,仅扫描与条件相关的分区数据,跳过无关的分区,大大减少了数据扫描量,提升查询速度。
假设我们有一张存储用户行为数据的表user_behavior
,按日期进行分区:
CREATE TABLE user_behavior (
user_id INT,
behavior_type VARCHAR(50),
event_time DATE
)
PARTITION BY RANGE(event_time) (
PARTITION p202401 VALUES LESS THAN ('2024-02-01'),
PARTITION p202402 VALUES LESS THAN ('2024-03-01')
)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES
(
"replication_num" = "1"
);
如果我们要查询 2024 年 1 月 15 日的用户行为数据:
SELECT * FROM user_behavior WHERE event_time = '2024-01-15';
Doris 的分区裁剪机制会识别出查询条件中的日期范围,只扫描p202401
分区的数据,而不会去扫描p202402
分区,极大地提高了查询效率。
优化建议:在设计表 Schema 时,充分考虑业务查询中常用的分区条件,合理设置分区。同时,在编写查询语句时,确保查询条件能够准确触发分区裁剪,避免全表扫描。
以上就是 Doris 查询优化秘籍上篇的主要内容,通过对 Schema、索引优化以及分区裁剪优化扫表的深入理解和运用,能够显著提升 Doris 在数据处理和查询方面的性能。下篇我们将继续探索 Doris 查询优化的其他技巧,敬请期待!