Doris 学习笔记

表类型

明细模型(Duplicate Key)

明细模型是 Doris 中的默认建表模型,用于保存每条原始数据记录。在建表时,通过 DUPLICATE KEY 指定数据存储的排序列,以优化常用查询。
特点:

  • 不去重也不聚合:与聚合模型与主键模型不同,明细模型不会对数据进行去重与聚合操作。即使两条相同的数据,每次插入时也会被完整保留。

聚合模型 (Aggregate Key)

特点​:

  • 导入数据时,相同的 Key 会自动合并,Value 列按照指定函数(SUM, MAX, MIN, REPLACE)聚合。
  • 可以使用 BITMAP_UNION()HLL_UNION()等函数对这些类型进行聚合。

​主键模型 (Unique Key)

特点​:

  • 保证设置的 Unique Key 的唯一性,新数据覆盖旧数据,类似MySQL。
  • Merge-on-Write (MoW): 支持写时合并。写入时做去重标记,查询速度极快(接近 Duplicate 模型)。
部分列更新

背景 ​: OLAP 数据库通常不支持 Update,或者 Update 代价极大(需要重写整行)。

机制 ​: 在 Unique Key (Merge-on-Write) 模型下,支持只更新部分字段,Doris 会自动补全其他字段的历史值。

使用方式(举例):

mysql 复制代码
-- 需要先设置 SET enable_unique_key_partial_update = true;
  INSERT INTO tbl (id, balance) VALUES (1, 500); -- 只更新余额,其他列不变

分区分桶

分区

作用 : 主要用于数据生命周期管理 (如按天、按月归档)和减少扫描范围。
自动设置(举例):

mysql 复制代码
PARTITION BY RANGE(event_time) ()
PROPERTIES (
    "dynamic_partition.enable" = "true",
    "dynamic_partition.time_unit" = "DAY",
    "dynamic_partition.start" = "-5",
    "dynamic_partition.end" = "3",
    "dynamic_partition.prefix" = "p",
    "dynamic_partition.create_history_partition" = "true" -- 创建历史分区
);

分桶

数据倾斜

现象 : 某个 BE 节点的负载远高于其他节点,或者某几个 Bucket 的数据量极大,导致长尾效应(整个查询速度取决于最慢的那个节点)。
排查 : 使用 SHOW DATA SKEW FROM table_name; 查看。
解决方案 :

  1. 更换分桶键 : 不要使用类似 city_id 或 date 这种低基数列做分桶,改用 uuid 或组合键。

  2. 增加分桶数 : 将大 Bucket 拆得更细。

  3. 使用 Random 分桶: 如果是聚合模型且不需要利用分桶键做 Join,可以使用随机分桶让数据绝对均匀。

分桶键(Distribution Key)错误

现象 :DISTRIBUTED BY HASH(comment_id)。但 video 表和 comment_agg 表都是按video_id 分布的。

如果 comment_table 按 comment_id 分布,那么同一个视频下的评论会被打散到集群的所有机器上。
Join 灾难 :当你执行 JOIN 查某个视频的评论+聚合数据时,数据库必须进行 Shuffle(网络重分布),这会产生巨大的网络开销,导致查询慢几倍甚至几十倍。
修正:必须改为 DISTRIBUTED BY HASH(video_id)。

Colocation Group (同组策略)

概念 : 将两张或多张表的分桶键(Bucket Key)、分桶数量、副本数量 完全一致,并将它们显式地归属于同一个 Group。
原理 : Doris 会保证这些表对应的 Bucket 落在相同的 BE 节点上。
优势 : 当这两张表进行 Join(且 Join Key = Bucket Key)时,直接在本地节点完成 Join,完全避免了网络 Shuffle,查询性能提升数倍。
用法:

mysql 复制代码
PROPERTIES (
    "colocate_with" = "group_video_core" -- 同组方便 Join
);

JOB机制

功能 : Doris 内置的任务调度器,可以定时执行 SQL。以前做 "定时调度"需要依赖外部定时任务,现在不需要了。
用法(举例):

mysql 复制代码
CREATE JOB my_daily_etl 
ON SCHEDULE EVERY 1 DAY 
STARTS '2025-01-01 02:00:00' 
DO 
	INSERT INTO target_table 
	SELECT * FROM source_table 
	WHERE date = yesterday();

注意STARTS时间不能用函数的形式,例如CURDATE()DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')

索引加速

前缀索引

Doris 会根据表的前 36 个字节(由列定义顺序决定)自动作为前缀索引。
建表顺序至关重要 : 将查询频率高、过滤度高的列(键)放在表、键定义的最前面。

倒排索引

用于解决非主键列文本模糊匹配高维多列任意组合查询 的性能问题。

支持等值、范围、列表查询加速。

支持全文检索(MATCH_ALL, MATCH_ANY)。
优势 : 相比前缀索引,它不依赖列的顺序;相比 BloomFilter,它不仅能过滤还能快速定位行号。
用法(举例):

mysql 复制代码
 CREATE INDEX idx_log_msg ON log_table(message) USING INVERTED PROPERTIES("parser" = "english"); -- 用 parser 可以指定分词
    ```
### 布隆过滤器

Doris 的数据是按列存储的,且被切分为多个 Page(页面)。Doris 会为指定的列在每个 Page 级别生成一个 Bloom Filter。查询时,系统先查这个小的索引结构,如果命中"不存在",则整个 Page 都不需要加载到内存。
**开启内置布隆过滤器(举例)**:
```mysql
PROPERTIES (
    "bloom_filter_columns" = "city,event_type",   -- 指定哪些列建 Bloom Filter
);

使用建议

  • 如果你的磁盘空间非常紧张,或者只是偶尔查询该列,用 布隆过滤器
  • 如果你对查询速度要求极高,且该列是核心查询条件,请优先使用 倒排索引

Array 类型

Apache Doris 中,如果你有一个 ARRAY<STRING> 类型的列(例如 video_tags ARRAY<STRING>),想要展开数组中的每个元素 (即"拿到每个数据"),需要使用 LATERAL VIEW + EXPLODE 函数。

mysql 复制代码
SELECT 
    video_id,          -- 假设你有视频 ID
    tag                -- 展开后的单个标签
FROM your_table
LATERAL VIEW EXPLODE(video_tags) t AS tag;

说明

  • EXPLODE(video_tags):将 video_tags 数组中的每个元素拆成一行。
  • LATERAL VIEW:用于将表生成函数(如 EXPLODE)的结果与原表关联。
  • t AS tag
    • t 是虚拟表别名(可省略或任意命名),
    • tag 是展开后每个元素的列名(你自定义)。

Bitmap 对比 HLL

特性 Bitmap (Roaring Bitmap) HLL (HyperLogLog)
精确度 100% 精确 (跟 count(distinct) 一样准) 非精确 (约 1%~2% 的误差)
数据类型要求 必须是整型 (Int/BigInt)。如果是字符串需构建全局字典映射。 任意类型 (String, Int, IP等均可)
存储空间 较小(但在高基数极其稀疏时比 HLL 大) 极小 (固定大小,非常节省)
是否支持分析 支持集合运算 (交集、并集、差集)。 能回答"看过A且看过B的人"。 不支持。只能回答"有多少人",无法知道"是谁"或做人群圈选。
计算速度 极快 (位运算) 极快 (概率算法)

什么时候必须要用 HLL?

  1. ID 是非数字且无法维护字典:
    如果你的统计口径是 Device ID (IMEI/IDFA/UUID),这些长字符串。Bitmap 只能存 Int。想用 Bitmap,你需要维护一张 String -> Int 的全局映射表(Global Dictionary)。在亿级实时流中,维护这个字典成本极高(需要 Redis 或 HBase 配合)。这时候用 HLL 最省事,直接 HLL_HASH(device_id) 即可。
  2. 超大规模、非关键指标的 Dashboard:
    比如全站实时总 PV/UV 大屏,数字在跳动,老板只看那个"量级"(是1亿还是1.1亿)。这时候为了节省内存和极致的写入速度,可以用 HLL。

踩坑指南

WSL启动脚本问题

网络模式问题 (WSL) :在 Windows 的 WSL 环境中,脚本检测为 Linux 系统,于是使用了 network_mode: host 。但在 Docker Desktop for Windows (WSL 2 backend) 中, host 网络模式并不能像原生 Linux 那样直接把端口暴露给 localhost (或者说支持得不够完美),这会导致你无法通过 127.0.0.1:9030 连接。

修改:统一 Linux 和 Mac 的配置,都使用 Bridge 网络 + 端口映射 ( ports ) 方式。这种方式在 WSL 2 下最稳定,也能确保你可以通过 127.0.0.1 正常访问。

ERROR 1105 (HY000): errCode = 2, detailMessage = replication num should be less than the number of available backends. replication num is 3, available backend num is 1

解释:Apache Doris 是一个 分布式数据库 ,设计之初就是为了生产环境的高可用性,默认 3 副本 是为了保证当其中一个节点宕机时,数据依然可用且不丢失。使用 Docker 单节点部署(只有 1 个 FE 和 1 个 BE),无法物理存储 3 个副本 (因为它需要 3 个不同的 BE 节点),所以使用默认设置建表会报错或无法成功。

解决方法:显式指定副本数为 1,末尾加上 PROPERTIES ("replication_num" = "1")

ERROR 1105 (HY000): errCode = 2, detailMessage = Merge-on-Write table's partition column must be KEY column

解释:进行分区时,如果用主键模型,分区列必须是主键列

解决方法:如果要用分区,必须把分区列加到主键列中,如果不想在主键中添加无关的属性就不要分区。

ERROR 1105 (HY000): errCode = 2, detailMessage = Time series compaction policy is not supported for unique key table

解释 :Doris 目前的 time_series Compaction 策略 仅支持 Aggregate 模型和 Duplicate 模型 , 不支持 Unique Key 模型 。

ERROR 1105 (HY000): errCode = 2, detailMessage = String Type should not be used in key column[main_category]

解释:STRING 类型在 Doris 中是 "大文本"(类似 Text/Blob),不支持作为主键。可以改成 VARCHAR 做主键,或者作为聚合列用 REPLACE 函数。

ERROR 1105 (HY000): errCode = 2, detailMessage = Insert has filtered data in strict mode. first_error_msg: no partition for this tuple.

解释:你的插入语句报错 no partition for this tuple ,这是因为:

  1. 你插入的数据时间是 21:00 (晚上9点)。
  2. 而表定义的动态分区参数是 "dynamic_partition.end" = "2" (小时)。
  3. 如果当前系统时间是上午(比如10点),分区只会预创建到 12:00 ,因此无法容纳 21:00 的数据。
    同时 Doris 的动态分区(Dynamic Partition)默认配置下,不会自动创建"历史分区"。(虽然你设置了 "dynamic_partition.start" = "-1",但在建表初始时刻,Doris 默认不回溯创建历史分区(默认参数 create_history_partition 为 false))
    需要设置 "dynamic_partition.create_history_partition" = "true"

ERROR 1105 (HY000): errCode = 2, detailMessage = Merge-on-Write table's partition column must be KEY column

解析:如果用主键模型,分区列必须是主键列

解决方法:如果要用分区,必须把分区列加到主键列中,如果不想在主键中添加无关的属性就不要分区。

ERROR 1105 (HY000): errCode = 2, detailMessage = Cannot invoke "org.apache.doris.nereids.trees.expressions.Expression.arity()" because "expr" is null

解释:Doris 的查询优化器(Nereids)在处理包含 CASE WHEN 的子查询分组时遇到了内部异常。

解决方法:我这个错误是因为内波查询用了 CASE WHEN 触发的,改成用 WITH 处理就可以了。

ERROR 1105 (HY000): errCode = 2, detailMessage = delete predicate on value column only supports Unique table with merge-on-write enabled and Duplicate table, but Table[video_event_agg] is an Aggregate table.

解释:在 Doris 中,Aggregate 表的设计初衷是用于预聚合写入,不支持对已聚合数据的任意删除。因为删除条件若涉及 value 列(如 cnt > 10),系统无法逆向还原原始明细数据,从而无法安全删除。

相关推荐
修炼者2 小时前
【Android进阶】 RenderEffect的底层实现
android
困死,根本不会2 小时前
Qt Designer 基础操作学习笔记
开发语言·笔记·qt·学习·microsoft
2501_926978332 小时前
LLM的可信度边界--人类思考的可信边界
经验分享·笔记·ai写作
WJSKad12352 小时前
Focus瓶颈轻量化改进YOLOv26通道压缩与残差学习协同突破
学习·yolo
在坚持一下我可没意见2 小时前
软件测试入门复习笔记:认识测试
软件测试·笔记·概念
xuhaoyu_cpp_java2 小时前
并发编程笔记2
笔记
愚者游世2 小时前
<algorithm> 中 remove、remove_if、remove_copy、remove_copy_if 详解
c++·学习·程序人生·职场和发展·visual studio
云边散步2 小时前
godot2D游戏教程系列二(13)
笔记·学习·游戏·游戏开发
gameboy0312 小时前
网络爬虫学习:应用selenium获取Edge浏览器版本号,自动下载对应版本msedgedriver,确保Edge浏览器顺利打开。
爬虫·学习·selenium