当前位置: 首页 > news >正文

以StarRocks为例讲解MPP架构和列式存储

一、 MPP架构和列式存储概念讲解

MPP 架构列式存储这两项技术是现代高性能数据分析数据库(如 StarRocks、ClickHouse、Snowflake 等)的基石,理解了它们,你就明白了为什么这些数据库能如此之快。


1. MPP 架构 (Massively Parallel Processing)

什么是 MPP?

MPP 即大规模并行处理。你可以把它想象成一个分工极其明确、协作效率极高的“工厂流水线”。

  • 传统数据库(如 MySQL):像一个老师傅,所有工作(接单、切菜、炒菜、装盘)都自己一个人完成。虽然也能做,但订单一多就忙不过来,成为瓶颈。
  • MPP 数据库:像一条现代化的流水线,有无数个工人(节点)同时工作。每个工人只负责处理一道工序,大家齐心协力,同时处理海量任务。
MPP 的核心思想:“分而治之”

MPP 架构将一个大型集群(多台服务器)中的每个节点都视作一个独立的计算和存储单元。没有一个全局的主节点会成为瓶颈。

  1. 数据分布:一张巨大的表(比如 100TB)的数据并不是存在一台机器上,而是被水平切分,分布到集群的数十、数百甚至数千个节点上。每个节点只存有一小片数据。
    • 常用策略:按照某个键(如 user_id)进行 Hash 分片,保证相同 user_id 的数据落在同一个节点上。
  2. 查询执行:当你执行一个查询时(例如 SELECT COUNT(*) FROM big_table):
    • 查询协调:一个主节点(Coordinator)接收你的 SQL 请求,并生成一个分布式执行计划。
    • 并行计算:它将这个计划下发到所有存有相关数据分片的节点上
    • 本地计算:每个节点在自己的那一小份数据上同时地、并行地执行这个计算(比如各自数自己那里有多少行)。
    • 结果汇总:各个节点将本地计算出的中间结果(比如各自的计数)返回给主节点。
    • 最终聚合:主节点将这些中间结果汇总成最终结果(把所有计数加起来),返回给用户。
MPP 的优势:
  • 高性能:多个节点并行工作,极大地缩短了处理时间。理论上,节点数量增加一倍,处理速度就能接近提升一倍(线性扩展)。
  • 高扩展性:可以通过增加廉价的普通服务器(节点)来轻松扩展整个集群的处理能力,以应对数据量和计算量的增长。
  • 无单点瓶颈:计算和存储都分散在各节点,没有共享资源争用的问题。
MPP 的劣势:
  • 延迟敏感:节点间需要频繁交换数据(Shuffle),因此对网络延迟要求很高,通常要求高速局域网。
  • 数据倾斜:如果数据分片策略不好(比如某个 user_id 的数据特别多),会导致某个节点任务过重,成为“短板”,影响整体速度。

2. 列式存储 (Columnar Storage)

什么是列式存储?

它与传统的行式存储相对应。

  • 行式存储:像把一行的所有数据(用户ID、姓名、年龄、地址…)紧紧挨着存在一起。类似于“记事本”,一行一行地记录。
    • 适用场景:适合事务处理(OLTP),如频繁插入、更新、删除整行数据的操作。例如 SELECT * FROM users WHERE user_id = 123
  • 列式存储:像把同一列的所有数据存在一起。所有用户ID存在一个地方,所有姓名存在另一个地方,所有年龄又存在别的地方。类似于“成绩单”,先列出所有学生的语文成绩,再列出所有学生的数学成绩。
    • 适用场景:适合分析处理(OLAP),如对某几列进行聚合、筛选、计算。例如 SELECT AVG(age), department FROM users GROUP BY department
列式存储的优势:
  1. 极高的压缩比:同一列的数据类型相同,数据模式相似(比如都是年龄数字,或者都是省份字符串),可以使用针对性的压缩算法(如 Run-Length Encoding、Delta Encoding、Dictionary Encoding),获得极高的压缩率。压缩不仅能节省存储空间,更能减少磁盘 I/O,因为从磁盘读出的数据块更小。
  2. 极少的 I/O 读取:分析查询通常只关心少数几个列。例如,计算平均年龄的查询只需要读取 age 这一列。
    • 行式存储:必须把整行数据(包括不需要的 name, address 等)从磁盘读入内存,浪费大量 I/O 带宽。
    • 列式存储:只需要读取 age 这一列的数据,I/O 效率极高。
  3. 更适合向量化执行:由于数据按列连续存储,CPU 可以一次性将一整列数据加载到高速缓存中,并利用 SIMD(单指令多数据)指令进行并行计算。这就像从“用勺子一粒一粒吃饭”变成了“用铲子一铲一铲吃饭”,极大地提高了 CPU 的计算效率。
列式存储的劣势:
  • 不适合频繁更新/点查:更新一行数据需要重写多个列的文件,成本很高。点查(查询单条记录的所有字段)也需要从多个列文件中读取数据并组装,性能较差。

3. 强强联合:MPP + 列式存储

像 StarRocks 这样的数据库,将两者结合,发挥了“1+1 >> 2”的效果:

  1. 数据存储:数据按列进行存储,并且被水平切分分布到 MPP 集群的各个节点上。
  2. 查询处理:当一个分析查询到来时:
    • MPP 架构的主节点生成分布式计划。
    • 每个数据节点并行地在自己本地的列式数据上进行操作。
    • 由于是列式存储,每个节点只读取查询所需的列,并利用向量化执行引擎极快地完成聚合、过滤等操作。
    • 最后将中间结果汇总,得到最终答案。
案例对比:

假设有一张 10 亿行、100 列的表,要执行 SELECT province, COUNT(*) FROM sales WHERE year = 2023 GROUP BY province

  • 传统行式数据库:可能需要从磁盘读取所有 100 列10 亿行数据(数据量巨大),然后在内存中过滤出 year=2023 的行,再对 province 分组计数。速度极慢。
  • MPP + 列式数据库
    1. 主节点将任务分发给所有存储节点。
    2. 每个节点只读取本地的 yearprovince 这两列的数据(I/O 量减少 98%)。
    3. 每个节点利用向量化执行,并行地过滤 year=2023 的数据,并对 province 进行本地计数。
    4. 各节点将(省份,计数)的中间结果发给主节点。
    5. 主节点做最终汇总。速度极快。

总结

特性MPP 架构列式存储
核心思想分而治之,并行处理按列组织数据
优化目标计算速度,通过增加节点线性扩展性能查询和压缩效率,减少 I/O,加速聚合
好比工厂流水线,众人拾柴火焰高成绩单,看单科成绩非常方便
劣势网络要求高,怕数据倾斜不适合频繁更新和点查

两者珠联璧合,共同造就了 StarRocks 在海量数据场景下极致的速度体验,使其成为实时数据分析的利器。

二、利用 MPP 和列式存储的特性进行表设计

在 StarRocks 中建表是性能调优的第一步,也是最关键的一步。巧妙地利用 MPP 和列式存储的特性进行表设计,能带来成倍的性能提升。

以下是如何在建表时更好地利用这两大特性的详细指南和最佳实践。


核心思路

  1. 利用 MPP:关键在于“数据分布”
    • 目标:让数据均匀分散在各个 BE 节点上,最大化并行计算能力;并尽量减少节点间数据传输(Shuffle)。
    • 手段:精心选择分桶键(DISTRIBUTED BY HASH)
  2. 利用列式存储:关键在于“降低I/O”和“加速计算”
    • 目标:减少扫描数据量、提高压缩比、优化本地计算效率。
    • 手段:选择合适的数据类型、编码、压缩,并设计分区排序键

1. 利用 MPP 架构进行数据分布设计

分桶(Bucketing) - DISTRIBUTED BY HASH(...)

这是实现 MPP 并行计算的基石。数据通过分桶键的 Hash 值被分散到不同的分桶(Tablet) 中,每个分桶都是数据移动、复制和计算的最小单元。

如何选择分桶键(Bucket Key)?

  • 原则一:选择高基数列
    • 为什么:确保数据能尽可能均匀地分布到各个分桶,避免数据倾斜。如果一个分桶的数据量远大于其他分桶,它就会成为查询的瓶颈。
    • 好的选择user_id, order_id, device_id 等唯一性或基数很高的列。
    • 坏的选择性别省份(基数低,会导致数据严重倾斜)、NULL(所有NULL值会hash到同一个桶)。
  • 原则二:选择频繁用于查询过滤或连接的列
    • 为什么:StarRocks 的本地计算能力很强。如果查询条件或 JOIN 条件中包含分桶键,可以极大减少节点间的数据传输(Shuffle)。
      • 例如,如果按 user_id 分桶,那么 WHERE user_id = 1001 的查询可以精准地只路由到一个分桶进行计算(Pruning)。
      • 如果两个大表都按相同的列(如 user_id)且相同桶数分桶,那么它们之间的 JOIN 操作可以采取 Colocate Shuffle 策略,直接在本地进行关联,效率极高。

如何确定分桶数量?

  • 推荐范围:单个分桶的数据量建议在 100MB ~ 1GB 之间。
  • 计算公式分桶数量 ≈ 总数据量预估 / 1GB
  • 限制:分桶数量一旦确定,后续不易修改。建表时需做好预估。对于中小表,建议设置不少于 8 个分桶以保证足够的并行度。

示例:

sql

CREATE TABLE user_behavior (user_id INT,item_id INT,...
)
DUPLICATE KEY(user_id, item_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 12 -- 选择高基数的user_id作为分桶键,并设置12个分桶
PROPERTIES ("replication_num" = "3");

2. 利用列式存储进行存储优化

选择合适的数据类型
  • 使用最小、最高效的数据类型
    • 使用 INT 而不是 BIGINT(如果数值范围允许)。
    • 使用 VARCHAR(n) 而不是 TEXT/STRING,并指定合适的长度。
    • 使用 DATEDATETIME 而不是字符串来表示时间。
  • 为什么:更小的数据类型占用更少的磁盘和内存空间,列式存储压缩效率更高,查询时在内存中能处理更多数据,CPU Cache 更友好。
使用编码和压缩 - encodingcompression

StarRocks 会自动为列选择编码和压缩(如 LZ4),但你也可以手动指定以优化性能。

  • 低基数列(如枚举值、状态码):使用 BITMAP_ENCODINGLOWCARD_DICT_ENCODING,压缩比极高。

  • 高基数列(如ID、时间戳):使用 PLAIN_ENCODINGDELTA_ENCODING

  • 可以在建表时指定:

    sql

    PROPERTIES ("replication_num" = "3","compression" = "LZ4", -- 表级别的压缩算法"storage_format" = "v2" -- 使用新版存储格式,通常更好
    );
    
排序键和前缀索引 - DUPLICATE/PRIMARY/UNIQUE KEY(...)

在 StarRocks 中,DUPLICATE KEYPRIMARY KEYUNIQUE KEY 决定了数据的排序和存储方式

  • 排序键(Sort Key):数据在每个分桶内是按照这些键排序后存储的。
  • 前缀索引(Prefix Index):StarRocks 会为每 1024 行数据生成一个前缀索引项,内容是排序键的前 36 个字节。查询时,可以先通过前缀索引快速定位到数据块,再在块内进行精准查找。

如何设计排序键?

  • 原则:将最常用作查询条件的列放在前面
  • 为什么:排序和前缀索引可以极大地加速范围查询和等值查询。
    • 查询 WHERE date = '2023-01-01' AND user_id = 100,如果 (date, user_id) 是排序键,可以快速定位到数据所在区域,极大减少需要扫描的数据行数

示例:

sql

CREATE TABLE user_behavior (date DATE,user_id INT,item_id INT,behavior_type VARCHAR(10),timestamp BIGINT
)
DUPLICATE KEY(date, user_id, item_id) -- 排序键:日期 -> 用户 -> 商品
DISTRIBUTED BY HASH(user_id) BUCKETS 12;

这个设计使得 WHERE date = '2023-01-01' 这类按天查询的效率非常高。


3. 分区(Partitioning) - PARTITION BY RANGE(...)

分区不是 MPP 的直接特性,但它是列式存储查询加速的重要补充

  • 作用:分区主要用于数据管理查询加速(分区剪枝)。
  • 如何工作:按照时间(通常是天)或其他范围将数据划分成不同的子目录。查询时,优化器可以根据条件直接跳过无关的分区,减少需要扫描的数据量。

分区键选择建议:

  • 几乎总是使用时间字段:如 datedt
  • 分区数量不宜过多:通常按天分区,单个分区数据量建议在 1GB 以上。过多的小分区会浪费元数据管理开销,影响 FE 性能。

示例:

sql

CREATE TABLE user_behavior (date DATE, -- 分区键user_id INT,...
)
DUPLICATE KEY(date, user_id, ...)
PARTITION BY RANGE(date) -- 按天分区
(PARTITION p20230101 VALUES [('2023-01-01'), ('2023-01-02')),PARTITION p20230102 VALUES [('2023-01-02'), ('2023-01-03')),...
)
DISTRIBUTED BY HASH(user_id) BUCKETS 12;

查询 WHERE date BETWEEN '2023-01-01' AND '2023-01-07' 只会扫描 p20230101p20230107 这 7 个分区,而不是全表。


综合实战案例:电商订单表

假设我们要创建一张订单事实表。

sql

CREATE TABLE dwd_order_fact (-- 时间维度 (分区键 & 排序键首位)dt DATE COMMENT "订单日期",order_time DATETIME COMMENT "订单时间",-- 业务维度 (高基数,作为分桶键和排序键)order_id BIGINT COMMENT "订单ID",user_id BIGINT COMMENT "用户ID",-- 其他维度product_id INT COMMENT "商品ID",province_code SMALLINT COMMENT "省份编码",-- 指标(度量)quantity INT COMMENT "数量",revenue DECIMAL(10,2) COMMENT "订单金额",coupon_amt DECIMAL(10,2) COMMENT "优惠券金额")
ENGINE=OLAP
-- 1. 列式存储优化:排序键设计,优先常用查询条件
DUPLICATE KEY(dt, order_id, user_id)
-- 2. MPP优化:选择高基数列user_id作为分桶键,数据均匀分布
DISTRIBUTED BY HASH(user_id) BUCKETS 16
-- 3. 分区管理:按天分区,方便管理并加速按天查询
PARTITION BY RANGE(dt)
(START ("2023-01-01") END ("2024-01-01") EVERY (INTERVAL 1 DAY)
)
-- 4. 存储属性设置
PROPERTIES ("replication_num" = "3","storage_format" = "v2","compression" = "LZ4"
);

设计解读:

  1. MPP并行:数据按 user_id Hash 分布到 16 个桶,均匀分散在集群各节点,并行计算。
  2. 列式存储
    • (dt, order_id, user_id) 排序,对按天查询和按订单/用户查询极度友好。
    • 使用合适的数据类型(如 SMALLINT 表示省份)。
    • 启用压缩。
  3. 分区裁剪:按天分区,查询特定日期范围时性能极佳。
  4. Colocate Join:如果用户维度表也按 user_id 分桶且桶数相同,两表关联时可避免Shuffle。

通过这样的设计,无论是面向用户的行为分析、按天的报表统计,还是多表关联查询,都能充分发挥 StarRocks MPP 和列式存储的威力。


文章转载自:

http://wXxbugsd.dfffm.cn
http://ICiaK2xg.dfffm.cn
http://UfTA3nHB.dfffm.cn
http://w0elFNao.dfffm.cn
http://fldesAQU.dfffm.cn
http://NNgOSR8e.dfffm.cn
http://uBLlnLiy.dfffm.cn
http://MgArpjGX.dfffm.cn
http://rG5HfIjq.dfffm.cn
http://dOnnW2rS.dfffm.cn
http://6THSmCAq.dfffm.cn
http://UZ7yMTWK.dfffm.cn
http://XIBFRfqK.dfffm.cn
http://sA6H3dVG.dfffm.cn
http://vCU9Cqn4.dfffm.cn
http://jFAqrkGX.dfffm.cn
http://g9edSVbk.dfffm.cn
http://VVLtKRyq.dfffm.cn
http://d7f25wFv.dfffm.cn
http://Wk0qBkHC.dfffm.cn
http://MQ3lRkjr.dfffm.cn
http://fseTpbru.dfffm.cn
http://JSNJRssU.dfffm.cn
http://KgpSpZnW.dfffm.cn
http://fXCvNdV7.dfffm.cn
http://NGuTq5Lg.dfffm.cn
http://N6Hwhvnd.dfffm.cn
http://KUZOOJol.dfffm.cn
http://xfuAajqg.dfffm.cn
http://4hsk7ulA.dfffm.cn
http://www.dtcms.com/a/367505.html

相关文章:

  • Kafka 学习教程:从基础概念到实践操作
  • 香港云主机常见使用问题汇总
  • 【图像处理基石】图像在频域处理和增强时,如何避免频谱混叠?
  • 【C++】17. AVL树实现
  • Java基础 9.4
  • 市政管网,各种规格的管件汇总大全
  • 【数据模型】思维导图的数据结构模型
  • 力扣字符串刷题-六道题记录-1
  • 【研究前沿】【书读多了,自然就聪明】人工智能中出现的智能涌现的原理是什么?为什么大模型能产生智能?能够泛化?深入了解背后的机制
  • ConvertAPI:PDF转Word的便捷之选
  • 正运动控制卡学习-点动
  • CodeBuddy+Lucene 探索与实践日志:记录我如何从零构建桌面搜索引擎
  • 虚拟化安全:从逃逸漏洞到实战分析
  • 实战演练(二):结合路由与状态管理,构建一个小型博客前台
  • Webus 与中国国际航空合作实现 XRP 支付
  • 专项智能练习(计算机动画基础)
  • webpack scope hositing 和tree shaking
  • AGX Orin平台RTC驱动导致reboot系统卡住问题调试
  • 期权平仓后权利金去哪了?
  • 基于深度掩码的动态模糊处理
  • claude code route 使用教程|命令大全
  • LeetCode 994 腐烂的橘子
  • 如何在 ONLYOFFICE AI 插件中连接智谱 AI
  • 【面试题】搜索准确性不高你怎么排查?
  • 静态电流Iq 和 ICONT_MAX
  • Redis在商城开发中起到什么作用?
  • 华为OD最新机试真题-可以处理的最大任务数-OD统一考试(C卷)
  • 学习嵌入式第四十六天
  • redis的hash表如何扩容
  • 单片机和PLC有哪些区别?揭秘单片机MCU的常见应用