MPP架构深度解析:从Shared Nothing原理到主流数据库选型

📅 发布时间:2026/10/7 10:58:10
MPP架构深度解析:从Shared Nothing原理到主流数据库选型
大概在四五年前我第一次被一张 SQL 卡到怀疑人生。业务方丢过来一个需求把三张千万级的事实表关联起来再按门店和品类做一组指标统计。我用 Oracle 跑了快四十分钟临时表空间都快撑爆了最后连结果都没等到。后来同事说要不把数据导到 Greenplum 里试试。我花了大半天导数同一个查询35 秒出结果。从那一刻起我才开始认真理解一个词——MPP。后来这些年我陆陆续续接触过 Teradata、Greenplum、Doris、StarRocks、ClickHouse也亲手搭过分析集群、排查过慢查询。回头看MPP 这个名词被很多人用得很含糊有人把它当成“分布式数据库”的代名词有人以为把一堆服务器堆起来就是 MPP还有人分不清 MPP 和 Hadoop 的区别。这篇是 MPP 系列的第一篇我打算先把最基本的问题讲清楚MPP 到底是什么它靠什么跑得快以及市面上有哪些主流的 MPP 平台。1. 为什么数据库会走向 MPP从单机瓶颈到 Shared Nothing1.1 单机数据库的极限在哪里无论是 Oracle 还是 MySQL单机版本都有一个隐性天花板数据量超过几个 TB 之后一次全表扫描可能要跑几十分钟甚至几小时内存装不下热数据磁盘 IO 就成了瓶颈CPU 核数再多单条 SQL 也吃不满。Scale Up纵向扩展的思路是买更强的机器更多 CPU、更大内存、更快磁盘但价格是指数级上升的而且物理上限就在那里——一台服务器撑死了也就几十路 CPU、几 TB 内存再往上就不是普通公司能扛的成本了。数据库领域很早就想过另一条路Scale Out横向扩展。既然单台机器做不大那就用一堆普通服务器组成集群每台机器只处理一部分数据合起来把任务干完。这里有个前提条件必须想清楚数据不是分开存就行了查询的时候怎么知道去哪台机器找数据怎么保证几十台机器算出来的结果能拼成最终答案这些问题的答案正是 MPP 架构的核心内容。1.2 Shared NothingMPP 的立身之本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 和 SegmentTeradata 里叫 PEParsing Engine和 AMPAccess Module ProcessorDoris/StarRocks 里叫 FEFrontend和 BEBackend。入口节点负责接收客户端请求、解析 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_idcustomers 是复制表那么每个节点都能在本地完成 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 与 VerticaTeradata 在我刚接触数据仓库那几年几乎就是 MPP 的代名词。它算是 MPP 商业化的鼻祖之一上世纪 80 年代就推出了 Shared Nothing 架构的数据库依靠 BYNET 高速互联网络把一批节点组织成一个集群表通过主索引Primary Index功能上类似分布键分布到所有 AMP 上去。金融、电信、零售行业的大型数仓里Teradata 是最常见的选择之一最大集群可以扩展到几百个节点。Vertica 则是列存 MPP 里很有代表性的产品。它把列式存储和 MPP 结合起来查询只需要读涉及的列压缩率也高分析场景里性能表现很亮眼。Vertica 被 HP 收购后又辗转到了 OpenText 手里现在仍然在服务不少老客户。从产品演进角度看Vertica 证明了“列存 无共享”的组合在分析型负载上比传统行存更有优势后来开源的 ClickHouse、Doris、StarRocks 等产品很多设计思路都能追溯到那个时代。3.2 开源与国产 MPPGreenplum、Doris、StarRocks 与 ClickHouseGreenplum 是我个人用得最多的开源 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 SegmentPostgreSQL 生态复杂 SQL、传统数仓高并发小查询弱运维偏重Doris / StarRocksFE BE向量化执行实时数仓、BI 加速复杂 JOIN 能力依赖模型设计ClickHouse分布式列存Distributed 表日志、流量、聚合查询分布式 JOIN 较弱SQL 有方言差异TeradataPE AMPBYNET 互联大型企业数仓商业授权贵软硬一体历史包袱Vertica列存 MPP分析加速商业授权生态在国内偏小众3.3 云上的 MPPRedshift、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 的部署、建表与调优把这篇里提到的坑挨个填上。