Doris 学习

本文详细介绍了Doris的MPP架构,包括节点并行、数据本地化和无共享特点,强调了其在大数据处理中的优势。此外,还探讨了向量化引擎的概念,解释了其内存效率、执行速度和并行处理的优势,以及在Doris中的应用,展示Doris如何实现高效的数据分析能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 知识点
    1. 概念、核心知识点
  1. MPP 架构:

MPP(Massively Parallel Processing)架构,即大规模并行处理架构,是一种分布式计算架构,它允许多个节点或处理器同时执行任务,以提高计算效率和处理速度。这种架构在大数据处理、数据仓库和分析等领域中非常常见,因为它能够有效地处理大量的数据和复杂的查询操作。

MPP架构的关键特点包括:

节点并行:在MPP架构中,数据被分布存储在多个节点上,每个节点都能够独立地执行计算任务。这样,当执行查询时,所有的节点可以并行地处理数据,从而显著提高整体的处理速度。

数据本地化:为了最大化并行处理的效率,MPP架构通常会尝试将计算任务分配给存储相关数据的节点。这种方式称为数据本地化,它可以减少数据在网络中的传输,从而降低延迟并提高性能。

无共享架构:MPP系统中的每个节点都是独立工作的,它们之间没有共享内存或存储。每个节点都有自己的内存和存储资源,这样可以避免节点之间的资源竞争,提高系统的稳定性和可扩展性。

弹性和容错性:MPP架构通常具备良好的弹性和容错性。系统可以通过增加更多的节点来扩展处理能力,同时,如果某个节点发生故障,系统可以重新分配任务到其他健康的节点上,保证系统的连续运行。

高性能分析:MPP架构特别适合进行复杂的分析查询,因为它可以并行执行多个计算密集型任务。这对于数据挖掘、机器学习和其他需要大量计算的应用场景非常有用。

在实际应用中,MPP架构被用于各种高性能计算场景,如Apache Doris等数据仓库系统就采用了MPP架构来提供快速的数据分析能力。通过这种方式,企业可以更有效地利用其数据资源,实现实时的业务洞察和决策支持。

  1. 向量化引擎:(列存储格式)

向量化引擎是一种数据处理技术,它通过将数据组织成列(向量)的形式来优化查询性能和资源利用效率。在传统的数据库系统中,数据通常以行(记录)为单位进行存储和处理。而在向量化引擎中,数据是按列存储的,每个列被视为一个独立的数据块,这样可以更有效地进行数据处理和压缩。 向量化引擎的主要优势包括:

内存效率:由于数据是按列存储的,向量化引擎可以利用现代CPU的SIMD(单指令多数据)功能,同时处理多个数据点,从而提高内存的使用效率。

执行速度:向量化查询可以减少CPU的指令数量,因为它们可以批量处理数据,而不是逐条记录处理。这种方式可以显著提高查询执行的速度。

减少I/O操作:向量化引擎在处理数据时,可以减少磁盘I/O操作的次数,因为它们可以更有效地加载和缓存列数据。

代码生成优化:向量化引擎可以在运行时动态生成针对特定查询的代码,这样可以进一步优化查询执行计划和性能。

并行处理:向量化引擎通常支持并行处理,可以充分利用多核处理器的能力,进一步提高数据处理的速度。 在Apache Doris等现代数据仓库系统中,向量化引擎是实现高性能分析查询的关键技术之一。通过使用向量化执行引擎,Doris能够提供快速、高效的数据分析能力,满足用户对实时大数据分析的需求。

  1. FE、BE:

Frontend(FE),主要负责用户请求的接入、查询解析规划、元数据的管理、节点管理相关工作。相当于 Hadoop 的 namenode

Backend(BE),主要负责数据存储、查询计划的执行。相当于 Hadoop 的 datanode。

  1. 数据模型

Aggregate模型 :聚合模型(优势及其局限性)

Unique模型 :唯一键模型(写时合并)

Duplicate模型 :明细模型

  1. 数据划分

Row & Column:行与列

Row:即用户的一行数据;

Column: 用于描述一行数据中不同的字段。Column 可以分为两大类:Key 和 Value。从业务角度看,Key 和 Value 可以分别对应维度列和指标列。Doris的key列是建表语句中指定的列,建表语句中的关键字'unique key'或'aggregate key'或'duplicate key'后面的列就是key列,除了key列剩下的就是value列。从聚合模型的角度来说,Key 列相同的行,会聚合成一行。其中 Value 列的聚合方式由用户在建表时指定。

Tablet & Partitio数据分片

用户数据被水平划分为若干个数据分片(Tablet,也称作数据分桶)。每个 Tablet 包含若干数据行。各个 Tablet 之间的数据没有交集,并且在物理上是独立存储的。多个 Tablet 在逻辑上归属于不同的分区(Partition)。一个 Tablet 只属于一个 Partition。而一个 Partition 包含若干个 Tablet。因为 Tablet 在物理上是独立存储的,所以可以视为 Partition 在物理上也是独立。Tablet 是数据移动、复制等操作的最小物理存储单元。多个Partition组合成一个Table。

分区分桶建议

一个表的 Tablet 总数量等于 (Partition num * Bucket num)。

一个表的 Tablet 数量,在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量

单个 Tablet 的数据量理论上没有上下界,但建议在 1G - 10G 的范围内。如果单个 Tablet 数据量过小,则数据的聚合效果不佳,且元数据管理压力大。如果数据量过大,则不利于副本的迁移、补齐,且会增加 Schema Change 或者 Rollup 操作失败重试的代价(这些操作失败重试的粒度是 Tablet)。

当 Tablet 的数据量原则和数量原则冲突时,建议优先考虑数据量原则

在建表时,每个分区的 Bucket 数量统一指定。但是在动态增加分区时(ADD PARTITION),可以单独指定新分区的 Bucket 数量。可以利用这个功能方便的应对数据缩小或膨胀。

一个 Partition 的 Bucket 数量一旦指定,不可更改。所以在确定 Bucket 数量时,需要预先考虑集群扩容的情况。比如当前只有 3 台 host,每台 host 有 1 块盘。如果 Bucket 的数量只设置为 3 或更小,那么后期即使再增加机器,也不能提高并发度。

举一些例子:假设在有10台BE,每台BE一块磁盘的情况下。如果一个表总大小为 500MB,则可以考虑4-8个分片。5GB:8-16个分片。50GB:32个分片。500GB:建议分区,每个分区大小在 50GB 左右,每个分区16-32个分片。5TB:建议分区,每个分区大小在 50GB 左右,每个分区16-32个分片。

注:表的数据量可以通过 SHOW DATA 命令查看,结果除以副本数,即表的数据量。

  1. Rollup

Rollup 可以理解为 Table 的一个物化索引结构。物化 是因为其数据在物理上独立存储,而 索引 的意思是,Rollup可以调整列顺序以增加前缀索引的命中率,也可以减少key列以增加数据的聚合度。

增加Rollup 的语句:ALTER TABLE table1 ADD ROLLUP rollup_city(citycode, pv);

ROLLUP 最根本的作用是提高某些查询的查询效率将其命名为 Materialized Index(物化索引)。

ROLLUP 是附属于 Base 表的,可以看做是 Base 表的一种辅助数据结构。用户可以在 Base 表的基础上,创建或删除 ROLLUP,但是不能在查询中显式的指定查询某 ROLLUP。是否命中 ROLLUP 完全由 Doris 系统自动决定。

ROLLUP 的数据是独立物理存储的。因此,创建的 ROLLUP 越多,占用的磁盘空间也就越大。同时对导入速度也会有影响(导入的ETL阶段会自动产生所有 ROLLUP 的数据),但是不会降低查询效率(只会更好)。

ROLLUP 的数据更新与 Base 表是完全同步的。

ROLLUP 中列的聚合方式,与 Base 表完全相同。在创建 ROLLUP 无需指定,也不能修改。

查询能否命中 ROLLUP 的一个必要条件(非充分条件)是,查询所涉及的所有列(包括 select list 和 where 中的查询条件列等)都存在于该 ROLLUP 的列中。否则,查询只能命中 Base 表。

某些类型的查询(如 count(*))在任何条件下,都无法命中 ROLLUP。

可以通过 EXPLAIN your_sql; 命令获得查询执行计划,在执行计划中,查看是否命中 ROLLUP。

可以通过 DESC tbl_name ALL; 语句显示 Base 表和所有已创建完成的 ROLLUP。

  1. 数据表查询内存限制 & 查询超时

为了防止用户的一个查询可能因为消耗内存过大。查询进行了内存控制,一个查询任务,在单个 BE 节点上默认使用不超过 2GB 内存。

可以通过:SET exec_mem_limit = 8589934592; 修改查询内存限制。

查询超时:当前默认查询时间设置为最长为 300 秒,如果一个查询在 300 秒内没有完成,则查询会被 Doris 系统 cancel 掉。用户可以通过这个参数来定制自己应用的超时时间,实现类似 wait(timeout) 的阻塞方式。可以通过:SET query_timeout = 60; 修改查询超时。

  1. Broadcast/Shuffle Join

系统默认实现 Join 的方式,是将小表进行条件过滤后,将其广播到大表所在的各个节点上,形成一个内存 Hash 表,然后流式读出大表的数据进行Hash Join。但是如果当小表过滤后的数据量无法放入内存的话,此时 Join 将无法完成,通常的报错应该是首先造成内存超限。

如果遇到上述情况,建议显式指定 Shuffle Join,也被称作 Partitioned Join。即将小表和大表都按照 Join 的 key 进行 Hash,然后进行分布式的 Join。这个对内存的消耗就会分摊到集群的所有计算节点上。

Doris会自动尝试进行 Broadcast Join,如果预估小表过大则会自动切换至 Shuffle Join。注意,如果此时显式指定了 Broadcast Join,则会强制执行 Broadcast Join。

  1. 索引

ColumnName

Type

user_id

BIGINT

age

INT

message

VARCHAR(100)

max_dwell_time

DATETIME

min_dwell_time

DATETIME

    1. 前缀索引

ColumnName

Type

user_name

VARCHAR(20)

age

INT

message

VARCHAR(100)

max_dwell_time

DATETIME

min_dwell_time

DATETIME

Doris 会将数据的前 36 个字节作为数据的前缀索引,但当遇到 Varchar 类型时,前缀索引会截断,例如:以下表结构的前缀索引为 user_id(Bigint 类型)(取8 Bytes) + age(Int 类型)(取4 Bytes) + message(prefix 20 Bytes)。

以下表结构的前缀索引为 user_name(20 Bytes)。即使没有达到 36 个字节,因为遇到 VARCHAR,所以直接截断,不再往后继续。

    1. 倒排索引

在Doris的倒排索引实现中,table的一行对应一个文档、一列对应文档中的一个字段,因此利用倒排索引可以根据关键词快速定位包含它的行,达到WHERE子句加速的目的。

与Doris中其他索引不同的是,在存储层倒排索引使用独立的文件,跟segment文件有逻辑对应关系、但存储的文件相互独立。这样的好处是可以做到创建、删除索引不用重写tablet和segment文件,大幅降低处理开销。

    1. BloomFilter 索引

布隆过滤器实际上是由一个超长的二进制位数组和一系列的哈希函数组成。二进制位数组初始全部为0,当给定一个待查询的元素时,这个元素会被一系列哈希函数计算映射出一系列的值,所有的值在位数组的偏移量处置为1。

下图所示出一个 m=18, k=3 (m是该Bit数组的大小,k是Hash函数的个数)的Bloom Filter示例。集合中的 x、y、z 三个元素通过 3 个不同的哈希函数散列到位数组中。当查询元素w时,通过Hash函数计算之后因为有一个比特为0,因此w不在该集合中。

那么怎么判断某个元素是否在集合中呢?同样是这个元素经过哈希函数计算后得到所有的偏移位置,若这些位置全都为1,则判断这个元素在这个集合中,若有一个不为1,则判断这个元素不在这个集合中。就是这么简单!

  1. 使用
  1. 表结构变更
  1. schema变更:略(与mysql类型)
  2. 替换表:

ALTER TABLE [db.]tbl1 REPLACE WITH TABLE tbl2
[PROPERTIES('swap' = 'true')]; 将表 tbl1 替换为表 tbl2。

如果 swap 参数为 true,则替换后,名称为 tbl1 表中的数据为原 tbl2 表中的数据。而名称为 tbl2 表中的数据为原 tbl1 表中的数据。即两张表数据发生了互换。

如果 swap 参数为 false,则替换后,名称为 tbl1 表中的数据为原 tbl2 表中的数据。而名称为 tbl2 表被删除。

  1. 分区信息

动态分区的规则参数都以 dynamic_partition. 为前缀:

dynamic_partition.enable

是否开启动态分区特性。可指定为 TRUE 或 FALSE。如果不填写,默认为 TRUE。如果为 FALSE,则 Doris 会忽略该表的动态分区规则。

dynamic_partition.time_unit(必选参数)

动态分区调度的单位。可指定为 HOUR、DAY、WEEK、MONTH、YEAR。分别表示按小时、按天、按星期、按月、按年进行分区创建或删除。

当指定为 HOUR 时,动态创建的分区名后缀格式为 yyyyMMddHH,例如2020032501。小时为单位的分区列数据类型不能为 DATE。

当指定为 DAY 时,动态创建的分区名后缀格式为 yyyyMMdd,例如20200325。

当指定为 WEEK 时,动态创建的分区名后缀格式为yyyy_ww。即当前日期属于这一年的第几周,例如 2020-03-25 创建的分区名后缀为 2020_13, 表明目前为2020年第13周。

当指定为 MONTH 时,动态创建的分区名后缀格式为 yyyyMM,例如 202003。

当指定为 YEAR 时,动态创建的分区名后缀格式为 yyyy,例如 2020。

dynamic_partition.time_zone

动态分区的时区,如果不填写,则默认为当前机器的系统的时区,例如 Asia/Shanghai,如果想获取当前支持的时区设置,可以参考 https://en.wikipedia.org/wiki/List_of_tz_database_time_zones。

dynamic_partition.start

动态分区的起始偏移,为负数。根据 time_unit 属性的不同,以当天(星期/月)为基准,分区范围在此偏移之前的分区将会被删除。如果不填写,则默认为 -2147483648,即不删除历史分区。

dynamic_partition.end(必选参数)

动态分区的结束偏移,为正数。根据 time_unit 属性的不同,以当天(星期/月)为基准,提前创建对应范围的分区。

dynamic_partition.prefix(必选参数)

动态创建的分区名前缀。

dynamic_partition.buckets

动态创建的分区所对应的分桶数量。

dynamic_partition.replication_num

动态创建的分区所对应的副本数量,如果不填写,则默认为该表创建时指定的副本数量。

dynamic_partition.start_day_of_week

当 time_unit 为 WEEK 时,该参数用于指定每周的起始点。取值为 1 到 7。其中 1 表示周一,7 表示周日。默认为 1,即表示每周以周一为起始点。

dynamic_partition.start_day_of_month

当 time_unit 为 MONTH 时,该参数用于指定每月的起始日期。取值为 1 到 28。其中 1 表示每月1号,28 表示每月28号。默认为 1,即表示每月以1号位起始点。暂不支持以29、30、31号为起始日,以避免因闰年或闰月带来的歧义。

dynamic_partition.create_history_partition

默认为 false。当置为 true 时,Doris 会自动创建所有分区,具体创建规则见下文。同时,FE 的参数 max_dynamic_partition_num 会限制总分区数量,以避免一次性创建过多分区。当期望创建的分区个数大于 max_dynamic_partition_num 值时,操作将被禁止。

当不指定 start 属性时,该参数不生效。

dynamic_partition.history_partition_num

当 create_history_partition 为 true 时,该参数用于指定创建历史分区数量。默认值为 -1, 即未设置。

dynamic_partition.hot_partition_num

指定最新的多少个分区为热分区。对于热分区,系统会自动设置其 storage_medium 参数为SSD,并且设置 storage_cooldown_time。

注意:若存储路径下没有 SSD 磁盘路径,配置该参数会导致动态分区创建失败。

hot_partition_num 是往前 n 天和未来所有分区

  1. 数据缓存
  1. PartitionCache:多个 SQL 使用相同的表分区时可命中缓存。(V2.1版本中属于测试功能)
  2. QueryCache
  3. SQLCache

特点:

  1. 用户无需担心数据一致性,通过版本来控制缓存失效,缓存的数据和从BE中查询的数据是一致的
  2. 没有额外的组件和成本,缓存结果存储在BE的内存中,用户可以根据需要调整缓存内存大小
  3. 实现了两种缓存策略,SQLCache和PartitionCache,后者缓存粒度更细
  4. 用一致性哈希解决BE节点上下线的问题,BE中的缓存算法是改进的LRU
  1. 自动分桶

旧版:DISTRIBUTED BY HASH(site) BUCKETS 20
新版:DISTRIBUTED BY HASH(site) BUCKETS AUTO properties("estimate_partition_size" = "100G")

新增的配置参数 estimate_partition_size 表示一个单分区的数据量。该参数是可选的,如果没有给出则 Doris 会将 estimate_partition_size 的默认值取为 10GB。

先根据数据量得出一个桶数 N。首先使用 estimate_partition_size 的值除以 5(按文本格式存入 Doris 中有 5 比 1 的数据压缩比计算),得到的结果为:

(, 100MB),则取 N=1

[100MB, 1GB),则取 N=2

[1GB, ),则每GB 一个分桶

根据 BE 节点数以及每个 BE 节点的磁盘容量,计算出桶数 M。其中每个 BE 节点算 1,每 50G 的磁盘容量算 1,那么 M 的计算规则为: M = BE 节点数 ( 一块磁盘块大小 / 50GB) 磁盘块数 例如有 3 台 BE,每台 BE 都有 4 块 500GB 的磁盘,那么 M = 3 (500GB / 50GB) 4 = 120

得到最终的分桶个数计算逻辑: 先计算一个中间值 x = min(M, N, 128), 如果 x < N并且x < BE节点个数,则最终分桶为 y 即 BE 节点个数;否则最终分桶数为 x

x = max(x, autobucket_min_buckets), 这里autobucket_min_buckets是在Config中配置的,默认是1。

  1. 查询计划
  1. EXPLAIN GRAPH select ...; 或者 DESC GRAPH select ...;:这些命令提供了执行计划的图形表示。它们帮助我们可视化查询执行的流程,包括关联路径和数据访问方法。
  2. EXPLAIN select ...;:这个命令显示指定 SQL 查询的执行计划的文本表示形式。它提供了有关查询优化步骤的信息,例如操作的顺序、执行算法和访问方法等。
  3. EXPLAIN VERBOSE select ...;:与前一个命令类似,这个命令提供了更详细的输出结果。
  4. EXPLAIN PARSED PLAN select ...;:这个命令返回 SQL 查询的解析后的执行计划。它显示了计划树和查询处理中涉及的逻辑操作符的信息。
  5. EXPLAIN ANALYZED PLAN select ...;:这个命令返回 SQL 查询的分析后的执行计划。
  6. EXPLAIN REWRITTEN PLAN select ...;:这个命令在数据库引擎对查询进行任何查询转换或优化后显示了重写后的执行计划。它提供了查询为了提高性能而进行的修改的见解。
  7. EXPLAIN OPTIMIZED PLAN select ...; 这个命令返回了 CBO 中得到的最优计划
  8. EXPLAIN SHAPE PLAN select ...;:这个命令以查询的形状和结构为重点,呈现了简化后的最优执行计划。
  1. 冷热数据分层

在Partition级别上设置freeze time,表示多久这个Partition会被freeze,并且定义freeze之后存储的remote storage的位置。在be上daemon线程会周期性的判断表是否需要freeze,若freeze后会将数据上传到s3和hdfs上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值