ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MPP架构深度解析:从Shared-Nothing到主流OLAP平台选型

MPP架构深度解析:从Shared-Nothing到主流OLAP平台选型 MPP这个词在数据库圈子里出现的频率越来越高从Greenplum到ClickHouse从Doris到StarRocks几乎每个跟大数据沾边的产品都要强调自己是MPP架构。但说实在的很多人对MPP的理解停留在“分布式”和“并行计算”这两个标签上真要问起来MPP到底怎么并行、跟其他架构有什么本质区别、不同平台的实现差异在哪能说清楚的人并不多。这篇是MPP系列的第一篇先把地基打牢。我会从MPP的核心定义讲起拆解它的架构设计逻辑再横向对比市面上主流MPP平台的特点和适用场景。这篇内容适合刚接触MPP的开发者也适合已经在用某个MPP产品、但想系统梳理架构原理的读者。看完这篇文章你至少能回答三个问题MPP为什么能跑得快、它在什么场景下会失效、主流平台之间到底差在哪里。1. 内容整体设计与思路拆解1.1 MPP到底是什么MPP全称是Massively Parallel Processing大规模并行处理。这不是一门新技术上世纪八九十年代就有了但在大数据时代被重新激活。理解MPP之前先看它解决的问题数据量到了一定规模之后单机数据库的算力、内存、磁盘都扛不住这时候有两个方向一种是像Hadoop那样把数据分散到大量廉价服务器上用分布式存储加分布式计算来硬扛另一种就是MPP它同样把数据分散到多个节点但核心目标不是“存储下放”而是“计算并行”。这里要拎清一个关键认知MPP的本质是让所有节点同时参与同一件计算任务而不是各干各的。传统分布式系统里你提交一个查询可能只有一个节点在处理其他节点闲着MPP的做法是把这张表按某种规则切成很多片每个节点只处理自己那一小片然后所有节点同时动手最后把结果汇总。听起来简单但“同时动手、各自处理、统一汇总”这十二个字直接决定了MPP的架构形态也决定了它的性能上限。1.2 对比中理解SMP、NUMA与MPP的定位差异不少人在看MPP资料时会碰到SMP和NUMA这两个缩写容易混淆。简单梳理一下这个谱系架构类型全称核心特征典型场景SMPSymmetric Multi-Processing多核CPU共享同一块内存、同一个总线单台服务器OLTP事务处理NUMANon-Uniform Memory Access多CPU各自绑定本地内存访问远端内存延迟更高单台高配服务器内存密集型计算MPPMassively Parallel Processing多台独立服务器各自有CPU、内存、磁盘通过网络互联海量数据OLAP分析SMP和NUMA解决的是“一台机器内部怎么让多个CPU高效协同”MPP解决的是“多台机器怎么协同处理一件事”。打个比方SMP是一间大办公室所有人共用一张大桌子NUMA是办公室被隔成几个小房间每个房间有自己的桌子但房间之间可以串门只是串门要花时间MPP是好几栋独立的办公楼每栋楼有自己的桌椅、仓库和厨房楼之间靠物流通道连接。这个对比很关键因为很多性能问题的排查思路完全不一样。比如SMP架构下性能下降大概率是内存带宽或锁竞争的问题MPP架构下性能下降先怀疑的是数据分布不均、网络带宽瓶颈、某个节点拖后腿排查方向天差地别。1.3 为什么今天需要重新理解MPP这两年MPP概念被频繁提起有三个直接原因。第一个原因是云原生数据仓库的兴起Snowflake、Redshift、阿里云AnalyticDB这些产品底层都是MPP思想在不同硬件形态上的实现理解MPP就成了理解云数仓的钥匙。第二个原因是国产数据库的密集落地从GaussDB到TDSQL从OceanBase到AnalyticDB几乎全都把MPP作为分布式查询引擎的底座不管最后对外怎么包装内核对MPP原理的依赖是实打实的。第三个原因是实时数仓的演进从离线批处理走向实时分析之后Doris、StarRocks、ClickHouse这些实时OLAP引擎不约而同选择了MPP路线这已经不只是“一条技术路线”而是整个大数据分析领域的主流共识。这也是我写这个系列的动机把MPP的原理讲透后面的产品实践、性能调优、问题排查才有根基。2. 架构深度拆解MPP是怎么跑起来的2.1 核心设计原则Shared-NothingMPP架构最核心的设计原则是Shared-Nothing无共享架构。这个词的字面意思就是“什么都不共享”每个节点拥有独立的CPU、内存和磁盘节点之间不共享任何存储资源只通过网络通信。Shared-Nothing带来的最大好处是水平扩展性。因为节点之间没有共享资源的冲突加一台机器就能线性增加算力和存储这在理论上让MPP集群的容量几乎无限。对比一下共享存储架构的Shared-Disk所有节点都得访问同一套存储设备存储就成了瓶颈加再多计算节点也绕不开这个公共瓶颈。但Shared-Nothing也付出代价。最大的代价是数据需要分布到各个节点而数据分布的方式直接决定查询性能。如果数据分布合理每个节点只需要处理自己那一份查询速度可以随节点数近似线性提升如果分布不合理某些节点数据特别多或者需要处理的数据特别多其他节点干完活了在那等着慢节点整体速度被短板拖死。SQL执行的时候MPP引擎会把一个查询拆成多个物理执行片段分发到不同节点上并行执行中间结果通过网络在节点间流转。这个流程中任何一个环节掉链子——某个节点负载高、网络抖动、数据倾斜——都会直接影响整个查询的响应时间。2.2 节点角色Coordinator与Worker的分工MPP集群内通常存在两类节点角色虽然不同产品叫法略有差异Greenplum叫Master和SegmentDoris叫FE和BEClickHouse叫Coordinator和Worker但分工逻辑是一致的。Coordinator节点也叫Master、FE是“大脑”。它负责接收用户SQL、解析语法、生成执行计划、把任务分发到Worker节点、汇总最终结果返回给客户端。Coordinator本身不存业务数据或者说只存元数据。这个设计有个实际好处Coordinator可以做得很轻量管理节点的增删、表结构的变更、权限控制都集中在这一个地方。Worker节点也叫Segment、BE是“手脚”负责实际存储数据分片、执行计算任务、把中间结果返回给Coordinator或者其他Worker。Worker节点的数量决定了集群的并行度也基本决定了计算性能的上限。这里有一个容易忽略的细节Coordinator是集群中的单点吗在早期MPP产品里确实是单点Master挂了集群就不可用。现在的主流产品基本都做了Coordinator的多活或者主备切换比如Doris的FE可以部署多个通过选举协议选出一个LeaderGreenplum的Master也有Standby节点。但从架构实现的复杂度来看Coordinator的可用性设计要比Worker节点麻烦得多因为所有查询都要经过它它一抖动全集群跟着抖。2.3 数据分布策略Hash、Round-Robin与RangeMPP要正常工作数据必须以某种策略打散到各个Worker节点上。这个策略的选择会直接影响查询效率我在实践中见过太多因为建表时数据分布策略选错导致后面查询慢得离谱的案例。三种主流分布策略Hash分布按某一列或多个列的哈希值取模把行分配到对应节点。这是最常用的策略核心价值在于“相同哈希值的行一定落在同一节点”所以如果两张表用相同的分布键做JOIN每个节点只需要和本地数据JOIN完全不需要跨节点传输数据这叫本地连接是MPP里性能最高的一种JOIN方式。Round-Robin分布按顺序轮流把行分配到不同节点不关心数据内容。这种策略写起来最简单数据也很均匀但有个致命缺点它无法保证两张表的数据落在同一节点导致几乎所有JOIN操作都得跨节点搬数据性能大打折扣。一般在临时表或者不需要JOIN的场景才会用。Range分布按某个列的取值范围划分数据到不同节点。这种策略适合有天然分区语义的数据比如按时间范围分片但容易造成数据倾斜——某个时间段的业务量特别大那个节点就累惨了。现在用得不多更多是作为Hash分布的补充。选择分布键是MPP建模里一等一的大事。我常用的经验是三个标准第一选择查询中最高频等值JOIN的列第二选择高基数的列比如用户ID比性别好得多第三选择数据分布相对均匀的列。满足了这三点80%的JOIN都能在本地完成性能会相当漂亮。2.4 并行执行引擎从SQL到分布式计划的转换MPP的快除了数据分散外还在于执行引擎把SQL“切片”的能力。一条SQL进入MPP引擎后大致经历这几个阶段第一阶段是SQL解析把文本变成语法树做语义校验。第二阶段是逻辑计划把语法树转成关系代数表达式这个阶段会做谓词下推、列裁剪等优化。第三阶段是物理计划决定用哪种JOIN算法、数据怎么在节点间流动、哪些步骤可以并行。第四阶段是把物理计划切成很多个执行片段分发到各个Worker。第五阶段是各Worker执行各自的片段通过流水线或阻塞方式向上游返回数据。JOIN的实现方式在MPP里尤其重要。最常用的两种是Partition-Wise Join和Shuffle Join。Partition-Wise Join的前提是两张表用了相同的分布键这样每个节点只需要处理本地数据没有网络传输成本。代价是建表时必须统一规划分布键。Shuffle Join则是先把两张表按JOIN键重新哈希分发让相同JOIN键的行落到同一个节点然后再在每个节点上做本地JOIN。灵活但中间多了一次全量数据的网络重分布数据量大时非常耗时。还有一个细节容易被忽略数据重分布的触发条件。不仅仅是JOINGROUP BY、DISTINCT、窗口函数中的PARTITION BY这些操作都可能需要跨节点重分布数据。MPP优化器的一个重要工作就是尽量消除不必要的重分布。很多SQL性能问题归根结底是重分布次数太多、重分布的数据量太大。3. 平台支持全景主流MPP产品横向对比3.1 传统MPP数据库Greenplum与VerticaGreenplum可以说是开源MPP数据库的代表它的架构是基于PostgreSQL做的SQL兼容性在MPP阵营里非常优秀。每个Segment节点运行一个独立的PostgreSQL实例通过Master节点统一调度。Greenplum的优势是生态成熟支持标准SQL、窗口函数、全文检索、GIS、机器学习扩展管理工具体系也完整。适合大规模的离线数仓场景尤其是有复杂SQL分析需求的场景。Vertica是列存数据库的先驱也是MPP架构。它的设计目标是极致压缩和查询速度Projection机制是它的特色。Vertica在金融行业、电信行业有过非常广泛的应用。但它近年的社区声音弱了不少加上商业授权比较严苛新项目中选它的团队明显少了。3.2 云原生数仓Snowflake、Redshift、AnalyticDBSnowflake是云数仓的标杆底层存储用对象存储计算层是无状态的虚拟仓库实现了存储和计算的完全分离。但它对MPP的实现方式是“虚拟化”的计算层实际上是MPP集群节点数可以动态伸缩查询时把需要的列从对象存储拉入计算节点的本地缓存。这种架构让Snowflake可以做到非常激进地弹性伸缩毕竟计算节点无状态随时拉起随时销毁。Redshift早期是纯MPP架构用计算节点的本地存储后来也演进出了Redshift Spectrum和RA3节点类型增加了存储和计算分离的能力。它的优势是跟AWS生态深度集成S3、Glue、EMR之间的数据流转很顺滑。阿里云AnalyticDB也是MPP架构底层基于自研的存储引擎兼容MySQL和PostgreSQL协议。在国内的实时数仓场景里用的非常多。它的特点是对高并发点查和实时写入优化比较多跟传统MPP偏向大查询的风格不完全一样。3.3 实时OLAP引擎ClickHouse、Doris、StarRocksClickHouse的架构经常被拿来跟MPP比较。严格来讲ClickHouse的定位是大规模并行分析引擎但它跟经典MPP有显著差异ClickHouse是多主架构没有Coordinator单点每个节点都可以接收查询请求然后由发起节点协调其他节点并行处理。这让它天然没有单点瓶颈但也让复杂JOIN和事务处理变得困难。Doris是国产开源MPP数据库架构是典型的FEBE两层。FE负责解析和调度BE负责存储和计算。Doris在实时数仓场景里表现出色支持高并发点查、实时导入、物化视图社区活跃度很高。StarRocks是从Doris fork出来的分支做了大量性能优化向量化执行引擎做得很彻底。StarRocks在复杂查询上的性能尤其多表JOIN场景在开源MPP里是第一梯队。如果你的核心场景是复杂的多维分析SQLStarRocks值得重点关注。3.4 分布式计算框架Hive、Spark与MPP的关系很多人会把Spark也归到MPP里实际上这是两类不同的东西。Spark的核心模型是弹性分布式数据集计算模型以批处理为主虽然也有SQL引擎但它的调度是“阶段式”的——每个Stage内有并行Stage之间有宽依赖要写入外部存储或shuffle后才能进入下个Stage。MPP的执行引擎则是常驻的、流水线式的。Worker进程长期运行一个查询的多个执行片段可以流水线衔接减少了中间落盘的开销。这带来的直接效果是MPP在交互式查询秒级到分钟级上明显优于Spark但在处理几百TB甚至PB级的ETL批处理场景时Spark的容错性和资源弹性更占优势。实际工程里有不少团队是两套并行跑的日常ETL清洗用Spark面向报表和Ad-hoc分析用MPP。这算不上“重复建设”本质上是两种查询模式的互补。4. 适用场景与选型逻辑4.1 MPP的强项场景MPP最适合的是大规模结构化数据的交互式分析。这里的“交互式”一般指查询要在秒级到几分钟内返回“大规模”指单表数据量在亿级到千亿级。典型场景包括用户行为分析、经营报表、实时监控大屏、存量数据多维探索。MPP也适合混合负载的场景。一套集群可以同时跑轻量的点查、中重度的多维分析、少量的数据导入。比如Doris和StarRocks可以支持每秒上千的并发点查同时支持每秒数万行的实时写入这种“既要又要”的能力传统Hadoop生态很难做到。4.2 MPP的弱势场景不要迷信MPP。有几种场景MPP不是最优解。高并发OLTP交易几千上万的TPS、大量短小SQL的在线交易这种活交给单机关系型数据库或者分布式事务数据库更合适MPP的节点间通信开销在短查询场景下反而成了劣势。超大规模ETL批处理几百GB甚至几个TB的输入数据要做复杂的清洗转换MPP的内存模型和容错机制不一定扛得住Spark或Hive的批处理框架在稳定性上更成熟。需要强一致性事务的跨节点操作MPP的分布式事务能力普遍偏弱如果业务需要跨节点的一致性修改原始架构上的“无共享”反而成了麻烦。4.3 选型框架从场景倒推产品我建议用四个维度来筛选MPP产品。第一个维度是SQL兼容性需求。如果业务里有大量复杂SQL、窗口函数、多表关联Greenplum和StarRocks的兼容性做得比较好ClickHouse对SQL的支持是增强版的一些标准SQL写法反而要绕弯。第二个维度是实时性要求。秒级甚至毫秒级分析响应优先考虑Doris、StarRocks、ClickHouse这类实时引擎分钟级以上的离线分析用Greenplum或HiveSpark也能接受。第三个维度是并发度。面向大量内部用户并发查询的Doris、StarRocks做得更好面向少量分析人员后台跑大查询的ClickHouse更省心。第四个维度是运维复杂度与部署形态。想上云就考虑Snowflake或云厂商的MPP数仓自己运维就选开源产品。这里我多提一句千万别既想云原生又想自己运维这个方向会让你的运维成本成倍增加不是在开玩笑。5. 常见问题与排查技巧实录5.1 数据倾斜MPP最经典的性能炸弹MPP查询变慢的第一嫌疑永远是数据倾斜。现象是查询卡住不动看集群监控发现某个Worker CPU打满、磁盘IO飙高其他节点闲得发慌。倾斜通常来自三方面第一分布键选择不当比如选了性别、地区这种低基数列某个值的数据占了全表的80%第二JOIN键本身有热点比如某个头部用户的数据量比其他用户高几个数量级第三SQL写法触发倾斜比如GROUP BY的字段基数太低聚合计算集中于少数节点。排查手段主要是看执行计划里有没有标志性的分布信息。Greenplum的EXPLAIN里可以看到每个节点的处理行数Doris的Profile里也可以看到每个BE的处理耗时。如果发现各节点处理数据量差异超过三倍基本可以判定为倾斜。解决方法看场景来倾斜发生在分布键本身的改表的重分布策略或者选高基数分布键倾斜发生在关联条件上的考虑加Random分布或换用更均匀的JOIN键倾斜发生在GROUP BY上的可以考虑先用子查询做局部聚合再在外层做二次聚合减少倾斜节点上的压力。5.2 JOIN性能劣化为什么关联越大越卡MPP里的JOIN最怕的是“重分布大结果集”。我排查过不少案例两张表都是千万级JOIN完之后还要做聚合结果查询跑了十分钟。打开执行计划一看Shuffle阶段在中间结果上产生了5倍的膨胀数据网络传输拖垮了整个集群。优化思路也有套路可循。第一检查两张表的分布键是否一致尽量让JOIN在本地完成。第二善用过滤条件下推把JOIN之前就能过滤的数据先过滤掉减小参与实际的JOIN数据量。第三考虑用Broadcast Join当小表数据量足够小比如几万行可以把小表复制到每个节点避免大表的全量shuffle。我踩过最深的坑是在Doris里加了一个大字段的SELECT导致Broadcast Join广播的内容暴涨网络直接被打满。后面把不需要的字段从查询里去掉性能立刻恢复。5.3 连接数风暴Coordinator节点的隐形杀手MPP集群最常见的事故不是磁盘满也不是CPU满载而是Coordinator的连接数被打爆。原因通常是业务侧用了满连接的连接池或者某个定时任务在高并发时段发起大量查询。第一次遇到这个问题时我以为是把连接池设置小了拉大之后反而更严重。后来意识到根因在查询模式一堆慢查询占着Coordinator连接不释放新查询不断涌入连接数指数级膨胀。处理办法分三层第一在业务侧限流给查询接口加并发信号量限制同时进行的查询数第二在数据库侧设置连接上限超出直接拒绝并提示重试第三优化慢查询从根本上降低查询占用的时间。对比排摸下来大多数场景下慢查询优化比单纯扩连接数有效得多。5.4 常见问题速查表问题表现最常见原因优先排查项兜底方案查询整体变慢数据倾斜各节点处理耗时差异调整分布键、二次聚合某条SQL特别慢JOIN触发大量重分布执行计划shuffle行数统一分布键、使用Broadcast Join集群CPU不高但响应慢网络带宽耗尽节点间传输量监控减少大字段查询、过滤条件下推Coordinator频繁告警连接数过多当前活跃连接数业务侧限流、优化慢查询写入变慢小文件太多数据文件大小与数量合并小文件、调整导入批次5.5 调优经验总结先看执行计划再看指标写MPP调优这么久我的心得是遇到性能问题第一步永远是看执行计划而不是急着调参数。执行计划告诉你的是“数据怎么流”参数调整只是在这个流动路径上做微调。路径错的话参数调得再激进都没用。具体看执行计划时重点抓三样东西每个步骤的估算行数有没有明显的行数膨胀点重分布操作出现在哪些步骤涉及的数据量多大是否有某个步骤行数预估和实际严重不符这是统计信息过期的典型信号。把这三样搞清楚至少能定位70%的性能问题。剩下30%可能是硬件层面的瓶颈比如磁盘、网络或者内存配置这时候再去翻监控和系统日志方向就清楚多了。写在最后的个人体会做MPP相关的工作这几年最深的感受是MPP不是一个“开箱即用”的技术它的性能上限很大程度上取决于你建表时的设计决策。分布键选错了后面怎么调优都费劲。一个经验是如果项目的核心查询逻辑涉及多张表的JOIN建表之前一定把所有高频JOIN的字段列出来选那个在大部分JOIN里出现且基数最高的字段作为分布键这个决策做对了后续性能会省很多心。另外想提醒一点MPP集群不是越大越好。节点多了节点间通信的开销也在增加。一些查询在8个节点的集群上跑得飞快扩展到32个节点反而变慢就是因为数据重分布的网络代价超过了并行计算的收益。容量规划时要结合实际查询特征来决定集群规模而不是盲目追求节点数。这个系列的内容还在继续写下一篇我准备深入讲讲MPP的执行计划与SQL优化重点拆解分布式JOIN的实现细节和优化器的决策逻辑。如果你在实际使用MPP的过程中也踩过什么坑或者有哪些场景拿不准怎么选型欢迎在评论区聊聊我看到了都会回复。
返回列表