☰
MPP架构深度解析:从Shared Nothing原理到主流数据库选型
2026/10/7 10:58:01 网站建设 项目流程

大概在四五年前,我第一次被一张 SQL 卡到怀疑人生。业务方丢过来一个需求:把三张千万级的事实表关联起来,再按门店和品类做一组指标统计。我用 Oracle 跑了快四十分钟,临时表空间都快撑爆了,最后连结果都没等到。后来同事说,要不把数据导到 Greenplum 里试试。我花了大半天导数,同一个查询,35 秒出结果。从那一刻起,我才开始认真理解一个词——MPP。

后来这些年,我陆陆续续接触过 Teradata、Greenplum、Doris、StarRocks、ClickHouse,也亲手搭过分析集群、排查过慢查询。回头看,MPP 这个名词被很多人用得很含糊:有人把它当成“分布式数据库”的代名词,有人以为把一堆服务器堆起来就是 MPP,还有人分不清 MPP 和 Hadoop 的区别。这篇是 MPP 系列的第一篇,我打算先把最基本的问题讲清楚:MPP 到底是什么,它靠什么跑得快,以及市面上有哪些主流的 MPP 平台。

1. 为什么数据库会走向 MPP:从单机瓶颈到 Shared Nothing

1.1 单机数据库的极限在哪里

无论是 Oracle 还是 MySQL,单机版本都有一个隐性天花板:数据量超过几个 TB 之后,一次全表扫描可能要跑几十分钟甚至几小时;内存装不下热数据,磁盘 IO 就成了瓶颈;CPU 核数再多,单条 SQL 也吃不满。Scale Up(纵向扩展)的思路是买更强的机器,更多 CPU、更大内存、更快磁盘,但价格是指数级上升的,而且物理上限就在那里——一台服务器撑死了也就几十路 CPU、几 TB 内存,再往上就不是普通公司能扛的成本了。

数据库领域很早就想过另一条路:Scale Out(横向扩展)。既然单台机器做不大,那就用一堆普通服务器组成集群,每台机器只处理一部分数据,合起来把任务干完。这里有个前提条件必须想清楚:数据不是分开存就行了,查询的时候怎么知道去哪台机器找数据?怎么保证几十台机器算出来的结果能拼成最终答案?这些问题的答案,正是 MPP 架构的核心内容。

1.2 Shared Nothing:MPP 的立身之本

MPP 的全称是 Massively Parallel Processing,大规模并行处理。它的核心思想,是数据库行业老前辈们很早就验证过的 Shared Nothing——无共享架构。无共享不是说节点之间完全不联系,而是指每个计算节点(通常是一台独立的物理服务器或容器)拥有自己独立的 CPU、内存和本地磁盘,节点之间通过高速网络互联,不共享内存,也不共享磁盘。

这跟传统的 Shared Everything(共享一切)正好相反。我们平时用的单体数据库,多核 CPU 共享同一块内存和磁盘,这是 SMP(对称多处理);如果进一步把服务器主板上不同区域的 CPU 和内存隔离开,就是 NUMA(非一致内存访问)。而 MPP 的做法更彻底:干脆把“一台大机器”拆成“很多台小机器”,每台小机器各管各的数据,遇到查询时一起干,干完把结果汇总。

用开饭店类比可能更好理解。SMP 模式是一个大厨房,所有厨师共享一张操作台和一套锅具;MPP 模式是连锁总店开了几十家分店,每家分店有自己的独立厨房,客户下了单,各家分店同时做自己负责的那部分菜,再由总店拼盘上桌。分店之间不用抢厨具,也不用等别人用完灶台,这就是“无共享”的意义。

1.3 先把概念理清:MPP 不是 Hadoop,也不是普通分布式应用

很多人把 MPP 和 Hadoop 混在一谈,其实两个路子差得很远。

维度MPP 数据库Hadoop / Spark
定位完整的分布式 SQL 数据库通用分布式计算框架 / 批处理引擎
数据存放预先按列哈希分布到各节点本地存储存放在 HDFS / 对象存储,多个副本
查询方式SQL 优化器生成计划,计算下推到数据所在节点任务切分分发,计算和存储经常分离
典型场景复杂 SQL 分析、即席查询、报表服务ETL、离线批处理、机器学习预处理
用户接触面SQL 客户端 / BI 工具编程接口、调度系统

Hadoop(包括后来的 Spark)更像“批处理计算框架”,把任务切成小块分发到节点执行,数据存在 HDFS 里,由多副本保证可靠性。MPP 则把数据预先按某个列 hash 分好片,查询计划由优化器生成,直接下推到各个节点执行。海量数据离线清洗、跑批任务,Hadoop/Spark 顺手;复杂 SQL 分析、即席查询、报表服务,MPP 顺手。两者不是替代关系,后面我会专门讲它们怎么写进同一条数据链路。

2. MPP 架构里的三件关键事:数据分布、执行机制、事务权衡

2.1 数据分布策略:分布式 SQL 的第一块基石

数据怎么分布,基本决定了 MPP 的上限。常见的分布策略有三种:哈希分布、复制表、随机分布。

哈希分布是 MPP 里最核心的一种。建表时指定一个或多个列作为分布键(Distribution Key),系统对分布键的值做哈希计算,再把行映射到具体节点上。这么设计有个天然好处:分布键值相同的行,一定落在同一节点。这对 GROUP BY、等值 JOIN 非常友好——只需要在本地节点算完自己那份,不用跨节点搬数据。

复制表适合小表,比如维度表。它的做法是每个节点都放一份完整副本,查询时任何节点都能本地 JOIN,完全避免网络传输。代价是写入时要同步到所有节点,所以只适合数据量小、更新不频繁的表。

随机分布则是不指定分布键,数据按轮询方式均匀散到各节点。数据是均匀了,但本地性没了——做 JOIN 大概率要重分布数据,性能通常最差,一般只用于临时表。

在 Greenplum 里建表大概是这样的:

CREATE TABLE orders ( order_id BIGINT, cust_id BIGINT, amount NUMERIC(12, 2), order_date DATE ) DISTRIBUTED BY (order_id); CREATE TABLE dim_customer ( cust_id BIGINT, cust_name VARCHAR(100) ) DISTRIBUTED REPLICATED;

orders 按 order_id 哈希分布,dim_customer 每个节点复制一份。但如果 orders 要和 dim_customer 按 cust_id 做 JOIN,而 orders 的分布键是 order_id 而不是 cust_id,那 JOIN 时 orders 就得按 cust_id 重新哈希一遍,把数据重分布到对应节点——这就是后面要说的 Redistribute Motion,也是 MPP 里最昂贵的操作之一。

2.2 一条 SQL 在 MPP 集群里的完整执行链路

MPP 集群通常有入口节点和计算节点两类角色。Greenplum 里叫 Master 和 Segment,Teradata 里叫 PE(Parsing Engine)和 AMP(Access Module Processor),Doris/StarRocks 里叫 FE(Frontend)和 BE(Backend)。入口节点负责接收客户端请求、解析 SQL、生成执行计划,并把计划分解成计划片段分发给所有计算节点。

拿一条 GROUP BY 查询举例:

SELECT region, SUM(order_amount) FROM orders WHERE order_date >= '2024-01-01' GROUP BY region;

如果 orders 按 region 做了哈希分布,各节点扫描本地数据,各自做 GROUP BY,然后把各自的(region, 汇总值)上传给入口节点,入口节点做一次最后的合并返回。整个过程只有一个汇总动作,网络开销很小。

换成 JOIN 场景就不一样了。orders 和 customers 按 cust_id 关联,如果 orders 的分布键是 order_id,customers 是复制表,那么每个节点都能在本地完成 JOIN,依然舒服;但如果两张都是大表、分布键又不一致,就必须把其中一张表按 cust_id 重新哈希,重分布到目标节点,或者把一小张表广播到所有节点去配对。

看执行计划时,只要看到 Motion 节点,就要警惕。MPP 里常见的 Motion 有三种:

  • Gather Motion:各节点把结果汇总到入口节点,开销小;
  • Redistribute Motion:节点间数据按新的哈希键重新分派,开销最大;
  • Broadcast Motion:把小表广播到所有节点,用于一表大一表小的 JOIN 优化。

我个人的经验是:调 MPP 查询性能,第一件事就是看执行计划里有没有 Redistribute Motion。Motion 越多,网络传输越大,查询越慢。常见的优化手段包括把高频 JOIN 键设为分布键、把大维表改成复制表、减少中间子查询产生的重分布。

2.3 分布式事务:MPP 在一致性上的取舍

分布式环境下,要让多个节点上的数据修改保持原子性和隔离性,需要两阶段提交(2PC)。准备阶段各节点锁定资源、写入日志并投票,提交阶段协调者根据投票结果决定提交或回滚。2PC 能保证一致性,但代价是高延迟和锁竞争,节点越多越明显。

所以 MPP 数据库通常不会往 OLTP 方向卷。你让它跑几千 TPS 的小事务、频繁点查,它不但不占便宜,反而因为分布式协调开销变成劣势。MPP 擅长的是“接一个大查询,所有节点火力全开几分钟”这种活儿。现在的 MPP / OLAP 产品基本都在这个边界内做文章:弱化分布式事务,强化导入性能和查询并发,用副本数撑可用性,而不是用强事务撑可靠性。

注意:如果你需要一个支持高频单行更新、强事务保证的数据库,MPP 不是正确答案。MySQL、PostgreSQL 这类单体 OLTP 数据库仍然是第一选择,必要时做分库分表。

3. 平台支持图谱:传统商用、开源阵营与云上引擎

3.1 传统商用 MPP 的元老:Teradata 与 Vertica

Teradata 在我刚接触数据仓库那几年,几乎就是 MPP 的代名词。它算是 MPP 商业化的鼻祖之一,上世纪 80 年代就推出了 Shared Nothing 架构的数据库,依靠 BYNET 高速互联网络把一批节点组织成一个集群,表通过主索引(Primary Index,功能上类似分布键)分布到所有 AMP 上去。金融、电信、零售行业的大型数仓里,Teradata 是最常见的选择之一,最大集群可以扩展到几百个节点。

Vertica 则是列存 MPP 里很有代表性的产品。它把列式存储和 MPP 结合起来,查询只需要读涉及的列,压缩率也高,分析场景里性能表现很亮眼。Vertica 被 HP 收购后又辗转到了 OpenText 手里,现在仍然在服务不少老客户。从产品演进角度看,Vertica 证明了“列存 + 无共享”的组合在分析型负载上比传统行存更有优势,后来开源的 ClickHouse、Doris、StarRocks 等产品,很多设计思路都能追溯到那个时代。

3.2 开源与国产 MPP:Greenplum、Doris、StarRocks 与 ClickHouse

Greenplum 是我个人用得最多的开源 MPP。它基于 PostgreSQL 内核改造,保留了完整的 SQL 支持,上手成本低。Master 负责生成执行计划并分发任务,Segment 节点做真正的计算和存储。早期版本基于 PostgreSQL 8.x,后来 Greenplum 7 已经把内核升级到 PostgreSQL 12,兼容性一直在改善。建表时用 DISTRIBUTED BY 指定分布键,用 DISTRIBUTED REPLICATED 做复制表。Greenplum 在金融、制造业数据仓库落地很多,性能口碑是“大数据量复杂查询稳”,短板是高并发小查询一般。

Doris 和 StarRocks 属于新一代的开源分析型数据库,核心是 FE + BE 的 MPP 架构,特征是向量化执行、支持实时导入、SSB / TPC-H 这类分析查询性能极高。Doris 源自百度开源项目,后来进了 Apache 孵化器;StarRocks 是从 Doris 社区演进出来的另一支,在实时更新、物化视图上做得更完善,现在也加入了 Linux 基金会。这类产品很适合“实时数仓、BI 加速、半结构化日志分析”的场景,也是我近几年推荐新手朋友优先尝试的方向。

ClickHouse 则比较特别。它本身是分布式列存引擎,通过 Distributed 表把查询分发到多个 Shard,本质上也是 Shared Nothing,但它和传统 MPP 不完全是一路:分布式 JOIN 能力相对弱,强项是单机性能极强、压缩率高、聚合查询极快。你可以把它理解成“使用 MPP 思想但更偏存储引擎”的分析工具。日志分析、流量统计这类场景,ClickHouse 非常能打;但如果你需要复杂多表 JOIN 和完整的 SQL 标准兼容,Doris / StarRocks 甚至 Greenplum 会更顺手。

产品架构特点适合场景需要留意的点
GreenplumMaster + Segment,PostgreSQL 生态复杂 SQL、传统数仓高并发小查询弱,运维偏重
Doris / StarRocksFE + BE,向量化执行实时数仓、BI 加速复杂 JOIN 能力依赖模型设计
ClickHouse分布式列存,Distributed 表日志、流量、聚合查询分布式 JOIN 较弱,SQL 有方言差异
TeradataPE + AMP,BYNET 互联大型企业数仓商业授权贵,软硬一体历史包袱
Vertica列存 MPP分析加速商业授权,生态在国内偏小众

3.3 云上的 MPP:Redshift、Snowflake 与国内云数仓

云上把 MPP 做成了服务,这是近十年变化最快的地方。AWS Redshift 是云上最经典的 MPP 数仓,架构是 Leader Node 加 Compute Node,列存加压缩,数据分布方式由用户选择 KEY / EVEN / ALL。Redshift 起步很早,很多用 AWS 的公司把它当默认数仓。

Snowflake 则代表了另一条路线:存储和计算彻底分离。所有数据放在对象存储之上,查询时拉起一个“虚拟数仓”(一组计算资源)执行,用完可以立刻释放。它用微分区管理数据,查询时做分区裁剪,严格说它不是传统 MPP,但在使用体验上,用户仍然像在用一个无限扩展的分析数据库。这种存算分离模式在云上越来越流行,Google BigQuery、国内阿里云 MaxCompute / AnalyticDB、腾讯云数仓等,基本都在往 serverless + 存算分离的方向演进。

如果你是自建机房、数据量在几十 TB 到 PB 级别,Greenplum 或 StarRocks 这类开源产品仍然很香;如果你在公有云上,优先看云厂商自研的 MPP 服务,省去扩缩容和数据重分布的运维苦活。

3.4 选型时我实际看重的四个维度

选型这件事很难给一个“永远正确的答案”,但我自己在判断时会看四个维度:

第一,查询复杂度。业务是大量多表 JOIN、复杂窗口函数,还是简单聚合?前者需要 SQL 兼容度高的 MPP,比如 Greenplum、Teradata;后者对引擎的 JOIN 能力要求没那么苛刻,ClickHouse 也能应付。

第二,实时性。导入到查询可见的时间延迟要求是分钟级还是秒级?秒级可见优先看 StarRocks / Doris 这类支持实时导入的引擎,Greenplum 更适合小时级批量加载。

第三,并发和弹性。BI 看板的并发查询会到多少?云上服务扩缩容很灵活,自建集群要考虑资源池和队列配置。

第四,运维成本。开源产品要自己扛集群、监控、升级、扩容数据重分布;云服务基本托管,但单价更贵。团队有多少人力和经验,这是硬约束。

4. MPP 不是银弹:边界、真实踩坑与湖仓一体配合

4.1 什么场景不该硬上 MPP

先说点容易上头的事。经常有人听说 MPP 快,就把所有系统都往 MPP 上迁。我见过最典型的失败案例,是一个订单系统的核心库要上 MPP,理由是“以后数据量会涨”。结果上线后,单笔订单写入要走分布式事务,延迟比原来高了两个数量级,业务方直接炸了。MPP 是为分析型负载设计的,不是为 OLTP 海量小事务设计的。

如果业务特征是单条或小批量 INSERT / UPDATE、高频按主键点查、强事务一致,请老老实实用 OLTP 数据库(MySQL、PostgreSQL、Oracle),必要时分库分表;MPP 更适合接数仓层,做 ETL 完成后的分析查询。另外,MPP 对高并发小查询也不友好:每个查询都要经过入口节点解析、生成计划、分发、汇总,调度开销很大。你拿它去扛几千 QPS 的简单查询,反而会发现自己成了瓶颈。

4.2 我踩过的三个坑:分布键、数据倾斜、扩容重分布

第一坑是分布键选错。有个朋友建表时把 status 字段当分布键,status 只有三种取值,集群 20 个节点,结果数据几乎全落在 3 个节点上。查询一跑,那三个节点 CPU 直接拉满,其他节点闲得在摸鱼。原因很简单:分布键的基数决定了数据是否均匀。分布键要选择基数高、且查询中频繁用于等值关联的列,比如 order_id、cust_id。如果找不到单一合适列,可以用多个列组成复合分布键。

第二坑是数据倾斜。做用户画像统计时,按 channel_id 分组,其中“外部投放”这个渠道占了四成数据量,跑半个小时跑不完。后来我对倾斜值做加盐拆分:给倾斜的键值拼一个随机后缀,把它拆到不同节点上;聚合时先按“盐值 + 业务键”计算中间结果,最后去掉盐值再汇总。性能提升了一个数量级。这个技巧在 MPP 里非常实用,遇到某个热点值拖垮全集群的场景,几乎就是标准解法。

第三坑是扩容重分布。Greenplum 加节点,期间要做数据重分布,把旧节点的数据按新集群的哈希规则重新算一遍。这个过程磁盘 IO 很高,查询性能明显下降,业务基本处于半暂停状态。后来再扩容,我都会提前评估数据量,选择业务低峰窗口,并且把容量规划做得更保守——从一开始预留 25%-30% 的余量,尽量推迟扩容时间,或者直接用云上支持在线弹性的 MPP 服务。

建议:在建表之前,先把业务最重的几条查询拿过来,分析它们的 JOIN 键和 GROUP BY 键,再决定分布键。这一步做对了,后面能省下无数调优时间。

4.3 湖仓一体:MPP 与 Hadoop / Spark 真的不是对手

现在常说的数据平台架构,基本是“湖”和“仓”各司其职:对象存储或 HDFS 存放原始数据和离线清洗结果,Iceberg / Hudi / Delta 这类表格式提供 ACID 和快照能力,Spark 负责复杂批处理和机器学习预处理,MPP 引擎(StarRocks、Doris、Greenplum 或云数仓)作为统一的加速分析层,支撑报表、即席查询和 BI 看板。

数据先进湖,再用 Spark 跑批落成宽表,最后把宽表同步到 MPP 里做分析,这是我见过最稳妥的一条数据链路。比如:Kafka 实时接入 -> Flink 加工 -> 写入 Hudi -> 定时同步到 StarRocks;或者离线链路:业务库同步到 HDFS -> Spark 清洗 -> 写 Iceberg -> 再同步到 Greenplum / Doris。MPP 在这里是“分析加速器”,而不是全家桶。

写到这里,MPP 的基本面貌应该清楚了:它是 Shared Nothing 架构下的大规模并行分析引擎,解决的是“数据大到单机装不下、查询复杂到单机算不动”的问题。我个人更深的体会是:很多 MPP 项目后面跑不动,不是引擎不够强,而是设计阶段没想清楚——分布键怎么定、查询模型什么样、数据多久导一次。下一篇我打算写 Greenplum 的部署、建表与调优,把这篇里提到的坑挨个填上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询