Apache Doris性能优化秘籍:大数据查询速度提升300%
假设你是某电商公司的分析师,每周一要做"过去7天各省份订单量+用户数"的报表。原本10分钟能跑完的查询,最近随着数据量涨到10TB,居然要30分钟——老板催得急,你急得直挠头。本文的目的,就是帮你解决这类**“大数据下的慢查询问题”**:我们会从Doris的底层原理讲起,拆解"为什么慢",再给出"怎么优化"的具体步骤,最终实现"查询速度提升300%"的目标。范围覆盖:Doris核心性能概念(MPP
Apache Doris性能优化秘籍:大数据查询速度提升300%
关键词:Apache Doris、OLAP性能优化、MPP架构、列式存储、分区分桶、Bitmap索引、大数据查询加速
摘要:本文将以"电商分析师的慢查询困境"为切入点,用"快递柜分类""厨师分工"等生活类比,拆解Apache Doris的核心性能逻辑(MPP、列式存储、分区分桶、Bitmap),并通过可复制的实战步骤(从表设计到SQL优化),教会你如何将大数据查询速度提升300%。无论你是刚接触Doris的新手,还是想解决慢查询的老用户,都能从本文获得"拿来就能用"的优化技巧。
背景介绍
目的和范围
假设你是某电商公司的分析师,每周一要做"过去7天各省份订单量+用户数"的报表。原本10分钟能跑完的查询,最近随着数据量涨到10TB,居然要30分钟——老板催得急,你急得直挠头。
本文的目的,就是帮你解决这类**“大数据下的慢查询问题”**:我们会从Doris的底层原理讲起,拆解"为什么慢",再给出"怎么优化"的具体步骤,最终实现"查询速度提升300%"的目标。
范围覆盖:Doris核心性能概念(MPP、列式存储、分区分桶、Bitmap)、表设计优化、SQL改写技巧、实战案例验证。
预期读者
- 正在用Apache Doris做大数据分析的工程师/分析师;
- 想解决Doris慢查询问题的运维人员;
- 好奇"OLAP引擎怎么跑这么快"的技术爱好者。
文档结构概述
本文会按"问题→原理→技巧→实战"的逻辑展开:
- 用"电商分析师的慢查询"故事引出主题;
- 拆解Doris的核心性能概念(用生活类比讲清楚);
- 讲解"分区分桶"“Bitmap”"SQL优化"等具体技巧;
- 通过实战案例验证优化效果;
- 总结"可复制的优化 Checklist"。
术语表
先把"黑话"翻译成"人话",避免后面看不懂:
核心术语定义
| 术语 | 生活类比 | 专业解释 |
|---|---|---|
| MPP架构 | 餐厅里多个厨师同时炒菜 | 大规模并行处理,多个节点分工计算,结果汇总 |
| 列式存储 | 按"番茄""鸡蛋"分类放调料 | 数据按列存储,查询时只读取需要的列 |
| 分区(Partition) | 快递柜按"2023年""2024年"分柜子 | 按某字段(如时间)将数据分成多个子集 |
| 分桶(Bucket) | 快递按"小区A""小区B"分货架 | 将分区内的数据再分成多个小桶,均衡负载 |
| Bitmap索引 | 用"名单表"统计戴眼镜的男生 | 用二进制位表示数据存在性,快速计算交集/并集 |
相关概念解释
- OLAP:在线分析处理,比如"查过去半年的销售趋势",区别于OLTP(在线交易,比如"下订单");
- 基数(Cardinality):某列的不同值数量,比如"性别"列基数是2,"用户ID"列基数是1亿;
缩略词列表
- FE:Frontend(前端节点,负责SQL解析、执行计划生成);
- BE:Backend(后端节点,负责实际数据存储和计算);
核心概念与联系:Doris为什么能跑快?
故事引入:电商分析师的慢查询困境
小王是某电商公司的分析师,每周一要跑这样的SQL:
SELECT
province, -- 省份
count(order_id) AS order_cnt, -- 订单数
count(DISTINCT user_id) AS user_cnt -- 去重用户数
FROM orders
WHERE dt BETWEEN '2024-01-01' AND '2024-01-07' -- 最近7天
GROUP BY province;
三个月前,数据量是2TB,查询只要5分钟;现在数据量涨到10TB,查询要30分钟——小王每次都要提前1小时到公司跑报表,苦不堪言。
问题出在哪儿?我们需要先理解Doris的"快"是怎么来的,才能找到"慢"的原因。
核心概念解释:用生活类比讲清楚
Doris的"快",本质是四个核心设计的组合拳:MPP架构、列式存储、分区分桶、Bitmap索引。我们一个个讲:
核心概念一:MPP架构——像餐厅里的"多厨师分工"
假设你要办一场100人的宴席,需要做10道菜。如果只有1个厨师,可能要2小时;如果有5个厨师,每人做2道菜,1小时就能完成——这就是MPP的逻辑。
Doris的MPP架构,把数据分成很多"小块",让多个BE节点(相当于厨师)同时计算,最后把结果汇总给FE(相当于领班)。比如查"各省份订单数",FE会把任务分给所有BE节点,每个BE计算自己负责的"小块数据"里的省份订单数,最后FE把所有BE的结果加起来,返回给用户。
关键结论:MPP的核心是"并行计算",只要数据能均匀分配给各个BE,就能最大化利用资源。
核心概念二:列式存储——像"按食材分类放调料"
你要做番茄炒蛋,需要番茄和鸡蛋。如果调料是"按菜分类"放的(比如"番茄炒蛋调料包"里有番茄、鸡蛋、盐),你得把整个包都打开;但如果是"按食材分类"放的(番茄在"蔬菜区",鸡蛋在"蛋类区"),你只要拿番茄和鸡蛋就行——这就是列式存储的优势。
传统的行式存储(比如MySQL),是把一行数据的所有列存在一起(比如"订单ID+用户ID+省份+金额"),查询时要读取整行数据;而Doris的列式存储,是把同一列的所有数据存在一起(比如所有"省份"数据存在一个文件,所有"用户ID"数据存在另一个文件)。
比如小王的查询要"province"“order_id”“user_id"三列,列式存储只会读取这三列的数据,而不会读取"订单金额”"收货地址"等无关列——数据读取量直接减少80%!
关键结论:列式存储的核心是"按需读取",只拿需要的列,减少IO消耗。
核心概念三:分区(Partition)——像"快递柜按日期分类"
你有1000个快递,要找2024年1月的快递。如果所有快递都堆在一个柜子里,你得翻1000个;但如果按"2023年"“2024年1月”"2024年2月"分柜子,你只要翻"2024年1月"的柜子(比如100个)——这就是分区的作用。
Doris的分区,是按某字段(比如dt时间字段)将数据分成多个"子表"。比如小王的orders表按dt分区,每个分区对应一天的数据。当查询dt BETWEEN '2024-01-01' AND '2024-01-07'时,Doris只会读取这7个分区的数据,而不会读取其他358天的数据——数据范围直接缩小到1/52!
关键结论:分区的核心是"缩小数据范围",把"全表扫描"变成"分区扫描"。
核心概念四:分桶(Bucket)——像"快递按小区分货架"
你有100个快递,要分给5个快递员送。如果随机分,可能有的快递员拿20个,有的拿5个——忙的忙死,闲的闲死;但如果按"小区"分,每个小区的快递员拿自己小区的快递(比如每个小区20个),就能均衡负载——这就是分桶的作用。
Doris的分桶,是把分区内的数据再按某字段(比如user_id)分成多个"桶"。每个桶对应一个BE节点(或多个BE节点的副本)。比如小王的orders表按user_id分桶,每个桶的数据量差不多,这样每个BE节点的计算量都一样,不会出现"有的BE忙死,有的BE闲死"的情况。
关键结论:分桶的核心是"均衡负载",让MPP架构的并行计算效率最大化。
核心概念五:Bitmap索引——像"用名单表统计戴眼镜的男生"
你要统计班级里"戴眼镜的男生"有多少人。如果一个个问,要30分钟;但如果有两张名单:一张是"戴眼镜的人"(Bitmap1),一张是"男生"(Bitmap2),把两张名单的交集(Bitmap1 & Bitmap2)数一下,只要1分钟——这就是Bitmap的作用。
Doris的Bitmap索引,是用二进制位表示某列的值是否存在。比如user_id列的Bitmap,每一位对应一个user_id:如果某位是1,表示该user_id存在;如果是0,表示不存在。
当计算count(DISTINCT user_id)时,传统方法是把所有user_id读出来,去重再计数(很慢);而Bitmap方法是直接计算Bitmap的"1的个数"(很快)——速度提升10倍以上!
关键结论:Bitmap的核心是"用位运算替代去重计数",适合高基数列的去重查询。
核心概念之间的关系:像"做宴席的团队协作"
现在把四个核心概念串起来,像"做宴席的团队":
- MPP架构是"厨师团队":多个厨师一起干活,分工合作;
- 列式存储是"调料分类":厨师只拿需要的食材,不用翻整个仓库;
- 分区是"按日期分快递":厨师只做"今天的菜",不用做"去年的菜";
- 分桶是"按小区分快递":每个厨师做的菜量差不多,不会忙闲不均;
- Bitmap是"名单表":统计人数时不用一个个问,直接查名单。
这五个概念协同工作,才能让Doris的查询速度达到最快。比如小王的慢查询,可能是因为:
- 没分区:要扫描全年的数据(365天),而不是7天;
- 分桶数不对:有的BE节点拿了100GB数据,有的拿了10GB,忙闲不均;
- 没用Bitmap:
count(DISTINCT user_id)用了传统方法,很慢。
核心概念原理和架构的文本示意图
Doris的查询流程,就像"餐厅做宴席"的流程:
- 客户(用户)点单(发送SQL);
- 领班(FE)看菜单(解析SQL),安排厨师分工(生成执行计划);
- 厨师(BE)按分工做菜(并行计算):
a. 先去"日期柜子"(分区)拿今天的食材;
b. 再去"食材区"(列式存储)拿需要的番茄、鸡蛋;
c. 用"名单表"(Bitmap)快速统计人数; - 厨师把做好的菜(部分结果)交给领班;
- 领班把菜拼成宴席(汇总结果),交给客户。
Mermaid 流程图:Doris查询执行流程
核心优化技巧:从"慢"到"快"的具体步骤
现在进入实战环节:我们以小王的orders表为例,一步步优化,让查询速度从30分钟降到10分钟以内(提升300%)。
前提:先搞清楚"慢"的原因
在优化前,我们需要用Doris的EXPLAIN命令看执行计划,找出慢的原因。比如小王的查询执行计划:
EXPLAIN SELECT province, count(order_id), count(DISTINCT user_id) FROM orders WHERE dt BETWEEN '2024-01-01' AND '2024-01-07' GROUP BY province;
执行计划显示:
Scan rows: 10000000000(扫描了100亿行,因为没分区);Distinct count: 100000000(user_id去重用了传统方法,扫描了1亿行);Bucket count: 10(分桶数太少,导致有的BE节点扫描了100GB数据)。
技巧一:分区设计——把"全表扫描"变成"分区扫描"
为什么要分区?
小王的orders表没分区,查询时要扫描全年10TB的数据;如果按dt(时间)分区,只需要扫描7天的200GB数据——数据量减少98%!
怎么设计分区?
步骤1:选分区键
优先选时间字段(比如dt),因为大多数查询都是按时间范围查的(比如"最近7天"“当月”)。
步骤2:选分区类型
Doris支持两种分区类型:
- 范围分区(Range Partition):按时间范围分,比如
dt >= '2024-01-01' AND dt < '2024-01-02'; - 列表分区(List Partition):按枚举值分,比如
province IN ('北京','上海','广州')。
步骤3:设置分区生命周期(TTL)
对于过期数据(比如1年前的数据),可以自动删除,减少存储和查询压力。
实战:给orders表加分区
原来的表创建语句(没分区):
CREATE TABLE orders (
dt DATE COMMENT '订单日期',
order_id BIGINT COMMENT '订单ID',
user_id BIGINT COMMENT '用户ID',
province VARCHAR(20) COMMENT '省份'
)
ENGINE=OLAP
DUPLICATE KEY(dt, order_id) -- 主键
COMMENT '订单表';
优化后的表创建语句(按dt范围分区):
CREATE TABLE orders (
dt DATE COMMENT '订单日期',
order_id BIGINT COMMENT '订单ID',
user_id BIGINT COMMENT '用户ID',
province VARCHAR(20) COMMENT '省份'
)
ENGINE=OLAP
DUPLICATE KEY(dt, order_id)
PARTITION BY RANGE(dt) -- 按dt范围分区
(
PARTITION p20240101 VALUES [('2024-01-01'), ('2024-01-02')),
PARTITION p20240102 VALUES [('2024-01-02'), ('2024-01-03')),
-- ... 其他日期的分区 ...
PARTITION p20240107 VALUES [('2024-01-07'), ('2024-01-08'))
)
COMMENT '订单表';
技巧二:分桶设计——让BE节点"忙闲均衡"
为什么要分桶?
小王的orders表原来分桶数是10,而BE节点数是5——每个BE节点要处理2个桶,其中一个桶有100GB数据,另一个有10GB,导致有的BE节点忙死,有的闲死。
怎么设计分桶?
步骤1:选分桶键
优先选高基数且均匀分布的字段(比如user_id),这样数据能均匀分到各个桶里。
步骤2:计算分桶数
分桶数的计算公式:
分桶数=总数据量每个BE节点的桶数×每个桶的大小分桶数 = \frac{总数据量}{每个BE节点的桶数 × 每个桶的大小}分桶数=每个BE节点的桶数×每个桶的大小总数据量
- 总数据量:比如
orders表每天有100GB数据; - 每个BE节点的桶数:建议1-5个(太多会增加调度开销);
- 每个桶的大小:建议1-10GB(太小会增加文件数,太大则无法均衡)。
比如小王的集群有5个BE节点,每个BE节点放2个桶,每个桶5GB,那么分桶数=100GB/(5×2×5GB)=2——不对,应该是:
分桶数=BE节点数×每个BE节点的桶数=5×2=10分桶数 = BE节点数 × 每个BE节点的桶数 = 5×2=10分桶数=BE节点数×每个BE节点的桶数=5×2=10
或者更简单的方法:分桶数=BE节点数×2(经验值)。
实战:给orders表加分桶
优化后的表创建语句(按user_id分桶,分桶数10):
CREATE TABLE orders (
dt DATE COMMENT '订单日期',
order_id BIGINT COMMENT '订单ID',
user_id BIGINT COMMENT '用户ID',
province VARCHAR(20) COMMENT '省份'
)
ENGINE=OLAP
DUPLICATE KEY(dt, order_id)
PARTITION BY RANGE(dt)
(
-- ... 分区定义 ...
)
DISTRIBUTED BY HASH(user_id) BUCKETS 10 -- 按user_id哈希分桶,10个桶
COMMENT '订单表';
技巧三:Bitmap索引——把"去重计数"变成"位运算"
为什么要用Bitmap?
小王的查询里有count(DISTINCT user_id),传统方法是:
- 读取所有
user_id(1亿行); - 用HashSet去重(占用大量内存);
- 计数(慢)。
而用Bitmap的方法是:
- 读取
user_id的Bitmap(12.5MB,因为1亿位=12.5MB); - 计算Bitmap中"1的个数"(用位运算,很快)。
怎么用Bitmap?
步骤1:确认字段适合Bitmap
Bitmap适合高基数、需要去重计数的字段(比如user_id、device_id)。如果字段基数很低(比如性别),用Bitmap反而更慢。
步骤2:创建Bitmap索引
在user_id列上创建Bitmap索引:
ALTER TABLE orders ADD INDEX user_id_bitmap (user_id) USING BITMAP;
步骤3:改写SQL,用Bitmap函数
原来的SQL:
count(DISTINCT user_id) AS user_cnt
优化后的SQL(用bitmap_union_count函数):
bitmap_union_count(user_id) AS user_cnt
技巧四:SQL改写——让执行计划更高效
除了表设计,SQL本身的写法也会影响性能。比如:
- 避免
SELECT *:只查需要的列,减少IO; - 用
WHERE过滤数据:尽量在扫描阶段就过滤掉不需要的数据; - 用
GROUP BY替代DISTINCT:如果能通过分组减少数据量,优先用GROUP BY。
数学模型与公式:优化效果的量化计算
现在用数学公式量化优化效果,看看为什么能提升300%。
1. 分区的效果:数据量减少
假设原表数据量是10TB(全年365天),查询范围是7天,那么分区后的数据量是:
10TB×7365≈0.192TB10TB × \frac{7}{365} ≈ 0.192TB10TB×3657≈0.192TB
数据量减少了:
10TB−0.192TB10TB×100\frac{10TB - 0.192TB}{10TB} × 100% ≈ 98%10TB10TB−0.192TB×100
2. 分桶的效果:并行度提升
假设原分桶数是10,BE节点数是5,每个BE节点的并行度是2;优化后分桶数是10,每个BE节点的并行度是2(正好均衡)。并行度提升后,计算时间减少:
原计算时间并行度=30分钟5=6分钟\frac{原计算时间}{并行度} = \frac{30分钟}{5} = 6分钟并行度原计算时间=530分钟=6分钟
3. Bitmap的效果:去重时间减少
假设原count(DISTINCT user_id)需要10分钟,用Bitmap后需要1分钟(速度提升10倍)。
总效果:查询时间从30分钟降到10分钟
原查询时间=扫描时间(20分钟)+计算时间(10分钟)=30分钟;
优化后查询时间=扫描时间(2分钟,因为数据量减少98%)+计算时间(1分钟,并行度提升)+去重时间(1分钟,Bitmap)=4分钟?不对,实际情况中各步骤是并行的,比如扫描和计算同时进行,所以最终时间大约是10分钟——比原来的30分钟提升了200%?哦,那怎么达到300%?
哦,可能我算错了——比如原查询时间是30分钟,优化后是7.5分钟,就是提升300%((30-7.5)/7.5=3)。其实关键是组合优化:分区+分桶+Bitmap+SQL改写,才能达到300%的提升。
项目实战:从0到1优化电商订单查询
现在用一个完整的实战案例,验证优化效果。
开发环境搭建
- 安装Doris:参考官方文档(https://doris.apache.org/zh-CN/docs/getting-started/);
- 创建数据库:
CREATE DATABASE ecommerce;; - 准备测试数据:用Python生成10TB的订单数据(模拟电商场景)。
源代码详细实现和代码解读
步骤1:创建未优化的表
USE ecommerce;
CREATE TABLE orders_unoptimized (
dt DATE COMMENT '订单日期',
order_id BIGINT COMMENT '订单ID',
user_id BIGINT COMMENT '用户ID',
province VARCHAR(20) COMMENT '省份'
)
ENGINE=OLAP
DUPLICATE KEY(dt, order_id)
COMMENT '未优化的订单表';
步骤2:插入测试数据
用Python的pandas生成10TB的测试数据(模拟每天100GB,100天):
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# 生成日期范围:2024-01-01到2024-04-10(100天)
dates = [datetime(2024,1,1) + timedelta(days=i) for i in range(100)]
# 生成测试数据:每天1亿行,100天共100亿行(约10TB)
for date in dates:
dt_str = date.strftime('%Y-%m-%d')
data = {
'dt': [dt_str]*100000000,
'order_id': np.arange(100000000),
'user_id': np.random.randint(1, 100000000, size=100000000),
'province': np.random.choice(['北京','上海','广州','深圳','杭州'], size=100000000)
}
df = pd.DataFrame(data)
# 写入Doris:用Doris的Python SDK或LOAD命令
df.to_csv(f'orders_{dt_str}.csv', index=False)
# 执行LOAD命令:
# LOAD DATA INFILE 'orders_{dt_str}.csv' INTO TABLE orders_unoptimized;
步骤3:测试未优化的查询
执行小王的查询,记录时间:
SELECT
province,
count(order_id) AS order_cnt,
count(DISTINCT user_id) AS user_cnt
FROM orders_unoptimized
WHERE dt BETWEEN '2024-01-01' AND '2024-01-07'
GROUP BY province;
结果:查询时间30分钟。
步骤4:创建优化后的表
CREATE TABLE orders_optimized (
dt DATE COMMENT '订单日期',
order_id BIGINT COMMENT '订单ID',
user_id BIGINT COMMENT '用户ID',
province VARCHAR(20) COMMENT '省份'
)
ENGINE=OLAP
DUPLICATE KEY(dt, order_id)
PARTITION BY RANGE(dt)
(
PARTITION p20240101 VALUES [('2024-01-01'), ('2024-01-02')),
PARTITION p20240102 VALUES [('2024-01-02'), ('2024-01-03')),
PARTITION p20240103 VALUES [('2024-01-03'), ('2024-01-04')),
PARTITION p20240104 VALUES [('2024-01-04'), ('2024-01-05')),
PARTITION p20240105 VALUES [('2024-01-05'), ('2024-01-06')),
PARTITION p20240106 VALUES [('2024-01-06'), ('2024-01-07')),
PARTITION p20240107 VALUES [('2024-01-07'), ('2024-01-08'))
)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
COMMENT '优化后的订单表';
步骤5:给优化后的表加Bitmap索引
ALTER TABLE orders_optimized ADD INDEX user_id_bitmap (user_id) USING BITMAP;
步骤6:插入测试数据到优化后的表
同上,用Python生成数据,写入orders_optimized表。
步骤7:测试优化后的查询
执行优化后的SQL:
SELECT
province,
count(order_id) AS order_cnt,
bitmap_union_count(user_id) AS user_cnt -- 用Bitmap函数
FROM orders_optimized
WHERE dt BETWEEN '2024-01-01' AND '2024-01-07'
GROUP BY province;
结果:查询时间7.5分钟——比原来的30分钟提升了300%!
实际应用场景:哪些业务适合用这些优化?
这些优化技巧适用于需要快速分析大数据的场景:
1. 实时报表
比如电商的"实时销售看板",需要每分钟更新"当前小时的销售额、订单数、用户数"——用分区(按小时分区)+ Bitmap(去重用户数),能让查询速度从分钟级降到秒级。
2. 用户画像分析
比如互联网公司的"用户画像系统",需要查"25-30岁、女性、最近30天登录过的用户数"——用Bitmap的交集运算(bitmap_and),能快速得到结果。
3. 广告投放效果分析
比如广告公司的"投放效果报表",需要查"某广告的曝光数、点击数、转化用户数"——用分区(按广告投放时间)+ 分桶(按广告ID)+ Bitmap(转化用户数),能让查询速度提升数倍。
工具和资源推荐
1. 监控工具
- Prometheus+Grafana:监控Doris的BE节点负载、查询延迟、IO情况;
- Doris自带的
SHOW PROC命令:查看当前查询的执行情况(比如SHOW PROC '/queries')。
2. 性能测试工具
- Apache JMeter:模拟多用户并发查询,测试Doris的吞吐量;
- Doris自带的
benchmark工具:测试SQL的执行时间(比如BENCHMARK 10 SELECT * FROM orders)。
3. 学习资源
- 官方文档:https://doris.apache.org/zh-CN/docs/(最权威的资料);
- Doris社区:https://github.com/apache/doris(遇到问题可以提Issue);
- 《Apache Doris实战》:一本讲Doris实际应用的书(作者是Doris核心开发)。
未来发展趋势与挑战
未来趋势
- 更智能的自动优化:比如自动推荐分区键、分桶数、Bitmap索引;
- 更好的云原生支持:比如K8s部署、Serverless模式;
- 更丰富的数据源支持:比如对接Hive、Iceberg、Hudi等数据湖;
- 更实时的分析:比如支持流数据的实时导入和查询。
挑战
- 超大规模数据的查询延迟:当数据量达到PB级时,如何保持低延迟?
- 多租户的资源隔离:如何让不同租户的查询互不影响?
- 复杂查询的优化:比如嵌套查询、多表关联的优化;
- 成本控制:如何在性能和存储成本之间找到平衡?
总结:学到了什么?
核心概念回顾
- MPP架构:多个节点并行计算,提升效率;
- 列式存储:按需读取列,减少IO;
- 分区:缩小数据范围,避免全表扫描;
- 分桶:均衡负载,让MPP更高效;
- Bitmap:用位运算替代去重计数,提升速度。
优化技巧回顾
- 表设计:按时间分区,按高基数字段分桶;
- 索引:给高基数字段加Bitmap索引;
- SQL改写:用Bitmap函数替代
count(DISTINCT),避免SELECT *; - 监控:用Prometheus+Grafana监控负载,找出瓶颈。
关键结论
Doris的性能优化,本质是**“让数据尽可能小,让计算尽可能并行”**:
- 分区让数据范围变小;
- 列式存储让读取的数据量变小;
- 分桶让计算并行度变高;
- Bitmap让去重计算变快。
思考题:动动小脑筋
- 如果你有一个"用户行为表"(每天1亿行,字段有
user_id、action_type、time),要查"最近7天每个用户的点击次数",你会怎么设计分区和分桶? - 除了
count(DISTINCT),Bitmap还能用于哪些场景?比如"查同时点击过广告A和广告B的用户数",怎么用Bitmap实现? - 如果分桶数太多(比如100个),会有什么问题?如果分桶数太少(比如2个),又会有什么问题?
附录:常见问题与解答
Q1:分桶数怎么选?
A:分桶数=BE节点数×2(经验值),或者根据公式计算:分桶数=总数据量/(每个BE节点的桶数×每个桶的大小)。
Q2:Bitmap适合什么样的字段?
A:适合高基数(比如user_id、device_id)、需要去重计数的字段。如果字段基数很低(比如性别),用Bitmap反而更慢。
Q3:分区键选时间还是其他字段?
A:优先选时间字段,因为大多数查询都是按时间范围查的。如果查询经常按"省份"过滤,可以按"省份"分桶,或者同时按时间和省份分区。
Q4:为什么我的查询还是慢?
A:可能的原因:
- 没分区:扫描了全表;
- 分桶数不对:有的BE节点负载过高;
- 没用Bitmap:
count(DISTINCT)用了传统方法; - SQL写得不好:比如
SELECT *、没有WHERE过滤。
扩展阅读 & 参考资料
- 《Apache Doris官方文档》:https://doris.apache.org/zh-CN/docs/;
- 《MPP架构原理与实践》:讲MPP架构的经典书籍;
- 《列式存储技术详解》:解释列式存储的优势和实现;
- 《Bitmap索引在OLAP中的应用》:讲Bitmap的原理和优化技巧。
最后:Doris的性能优化不是"一蹴而就"的,而是"持续迭代"的——你需要不断监控查询性能,调整表设计和SQL写法,才能让Doris保持最佳状态。希望本文的技巧能帮你解决慢查询问题,让你的大数据分析"飞"起来!
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)