<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>分布式系统 on FXIO 技术博客</title><link>https://fxio.site/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/</link><description>Recent content in 分布式系统 on FXIO 技术博客</description><generator>Hugo -- 0.157.0</generator><language>zh-cn</language><lastBuildDate>Tue, 04 Aug 2026 12:20:12 +0800</lastBuildDate><atom:link href="https://fxio.site/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/index.xml" rel="self" type="application/rss+xml"/><item><title>平衡的艺术：从美食风味到分布式数据库架构设计</title><link>https://fxio.site/posts/ai/2026-08-04-balance-art-food-and-database/</link><pubDate>Tue, 04 Aug 2026 12:20:12 +0800</pubDate><guid>https://fxio.site/posts/ai/2026-08-04-balance-art-food-and-database/</guid><description>&lt;h1 id="平衡的艺术从美食风味到分布式数据库架构设计"&gt;平衡的艺术：从美食风味到分布式数据库架构设计&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;世上没有单项满分的系统，也没有包揽百味的菜肴。无论是在厨房还是在机房，「平衡」永远是衡量顶尖水平的核心标准——区别只在于，厨师的取舍写在菜单上，架构师的取舍写进事故复盘里。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="一评委那句味道不平衡和-cto-那句架构不对是同一句话"&gt;一、评委那句「味道不平衡」，和 CTO 那句「架构不对」是同一句话&lt;/h2&gt;
&lt;p&gt;看过美食竞技节目的人都熟悉这个场面：选手端上一道堆满昂贵食材的大菜，评委尝了一口，摇头：「技术没问题，但味道不平衡。」&lt;/p&gt;
&lt;p&gt;选手往往不服：我用了最好的食材啊。&lt;/p&gt;
&lt;p&gt;评委的反问一针见血：&lt;strong&gt;好食材的堆砌，什么时候等于好菜了？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;做了这么多年数据架构，我发现自己给系统做评审时，嘴里冒出来的话和美食评委几乎一字不差。团队兴冲冲地演示：「我们上了 Kafka、Flink、Spark、Hive、Presto、ClickHouse、Redis、Elasticsearch……全套大数据组件！」我尝了一口，摇头：技术选型都没问题，但这个架构不平衡。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;烹饪与数据库架构设计，本质上是同一件事：在有限的约束条件下（预算、时间、物理定律），用一堆各有脾气的原料，为一群口味明确的食客，端出一道协调的菜。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;约束永远存在。厨师逃不开「酸甜咸辣鲜」五味相生相克的化学规律，架构师逃不开 CAP 定理和物理硬件的性能边界。真正的功夫，不在于手里有多少好东西，而在于&lt;strong&gt;敢不敢为了整体的平衡，放弃局部的炫技&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;下面，我们拿 PrestoDB 当主菜，一道道拆开看。&lt;/p&gt;
&lt;h2 id="二风味与性能用咸提鲜用酸解腻用放弃换速度"&gt;二、风味与性能：用咸提鲜，用酸解腻，用「放弃」换速度&lt;/h2&gt;
&lt;h3 id="厨房里的相生相克"&gt;厨房里的相生相克&lt;/h3&gt;
&lt;p&gt;中餐调味有句老话：「咸为骨，酸为魂。」做红烧肉，最后那一点点盐不是为了让菜变咸，而是把肉的鲜味「托」出来；做糖醋排骨，酸的作用不是刺激，而是解掉糖和油脂的腻。&lt;strong&gt;每一种味道的出场，都是为了成全另一种味道——这就是风味平衡的本质：没有一种味道是为自己而存在的。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="机房里的-cap-与-pacelc"&gt;机房里的 CAP 与 PACELC&lt;/h3&gt;
&lt;p&gt;分布式系统有个对应物。入门先学 CAP：一致性、可用性、分区容忍性三选二。但实战中更有用的是它的延伸——&lt;strong&gt;PACELC&lt;/strong&gt;：即便没有分区（Partition），系统也必须在延迟（Latency）和一致性（Consistency）之间二选一。&lt;/p&gt;
&lt;p&gt;翻译成人话：**天下没有又绝对一致、又绝对快的数据访问，正如没有又极酸又极甜还不腻的酱汁。**你总得决定，这一勺下去，主味是什么。&lt;/p&gt;
&lt;h3 id="presto-的调味方案"&gt;Presto 的调味方案&lt;/h3&gt;
&lt;p&gt;2012 年 Facebook 造 Presto 时，面对的食材是 Hive/MapReduce：每个计算阶段的中间结果必须落盘存档，任何机器挂了都能从磁盘恢复重来。这份容错像一大勺盐——在批处理的场景里它是骨架，必不可少；但在交互式查询里，它把「快」这个主味彻底压死了。&lt;/p&gt;
&lt;p&gt;Presto 的做法是果断减盐：&lt;strong&gt;放弃 Task 级中间落盘与断点重试，换上全内存流水线&lt;/strong&gt;——数据以「页」为单位在算子间直接流动，全程不落盘。代价写得明明白白：一个 Worker 挂掉，查询失败只能整体重跑。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;顶级厨师知道哪一勺盐该省，顶级架构师知道哪一份容错该扔。Presto 省下的是落盘开销，托出来的是秒级响应的「鲜」。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这笔账 Presto 算得很清楚：交互式查询天然短平快，为 99% 的短查询付 100% 的容错成本，就像给每道快炒都上高汤慢煨的功夫——感动了自己，腻死了食客。&lt;/p&gt;
&lt;h2 id="三食材与模块和牛配松露为什么反而是灾难"&gt;三、食材与模块：和牛配松露，为什么反而是灾难&lt;/h2&gt;
&lt;h3 id="昂贵食材的无脑堆砌"&gt;昂贵食材的无脑堆砌&lt;/h3&gt;
&lt;p&gt;美食圈有个经典反面教材：和牛 + 松露 + 鱼子酱，三样顶配堆在一个盘子里。结果呢？和牛的脂香被松露的霸道盖住，鱼子酱的咸鲜又和前两者打架——三种顶级食材互相拆台，最后谁也没赢。&lt;/p&gt;
&lt;p&gt;**好菜的秘诀从来不是食材多贵，而是互补与留白。**一碗开水白菜，汤是扫过三遍的清汤，菜是只取菜心的嫩叶，没有任何昂贵成分，却成了国宴名菜——因为它知道该放什么，更知道该留白什么。&lt;/p&gt;
&lt;h3 id="大数据组件的和牛松露综合征"&gt;大数据组件的「和牛松露综合征」&lt;/h3&gt;
&lt;p&gt;这个病症在机房里一样流行。我见过太多这样的架构：为了「先进」，给每个环节都上顶配——数据要实时入湖、计算要流批一体、查询要即席秒回、还要顺手上个向量检索。组件之间互相打架：实时管道抢占批处理资源，分析引擎和业务库抢 I/O，最后整个系统像那盘堆料大菜，每个组件单拎出来都很强，合在一起谁都不舒服。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Presto 是这道题的反向满分答案：它干脆不吃食材这碗饭。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;作为查询引擎，Presto 做了一个当年看起来很「怂」的决定——&lt;strong&gt;自己不存任何数据&lt;/strong&gt;。没有专用存储格式，没有私有数据布局，通过 Connector 插件对接外部存储：Hive/HDFS、S3、MySQL、Kafka、Cassandra……想吃哪块地里的菜，装个对应的插头就行。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;精简的计算层 + 多变的存储层，像清汤配菜心：计算只管把味道吊出来，数据的风味保留在原产地。&lt;/p&gt;</description></item><item><title>从 Hive 到 Presto：一个「快」字背后，是三次豪赌式的设计取舍</title><link>https://fxio.site/posts/ai/2026-08-04-presto-architecture-philosophy/</link><pubDate>Tue, 04 Aug 2026 12:14:32 +0800</pubDate><guid>https://fxio.site/posts/ai/2026-08-04-presto-architecture-philosophy/</guid><description>&lt;h1 id="从-hive-到-presto一个快字背后是三次豪赌式的设计取舍"&gt;从 Hive 到 Presto：一个「快」字背后，是三次豪赌式的设计取舍&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;2012 年，Facebook 的工程师们受够了「跑个查询去喝杯咖啡」的日子。Presto 的诞生不是又一个 SQL 引擎的堆料，而是一连串清醒的放弃。本文用「舍与得」的视角，拆开 Presto 三项核心架构决策——以及它们各自的账单。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="引言大数据时代的等待焦虑"&gt;引言：大数据时代的「等待焦虑」&lt;/h2&gt;
&lt;p&gt;先讲一个老故事。&lt;/p&gt;
&lt;p&gt;2012 年前后，任何在 Facebook 规模的数据仓库上工作过的人，都熟悉这种体验：你写了一段 SQL，想看看昨天某个功能的用户留存。提交给 Hive，底层翻译成 MapReduce，然后——&lt;/p&gt;
&lt;p&gt;等。&lt;/p&gt;
&lt;p&gt;第一个 Map 阶段读数据、算完、&lt;strong&gt;写磁盘&lt;/strong&gt;；第二个 Reduce 阶段再读磁盘、算完、&lt;strong&gt;再写磁盘&lt;/strong&gt;；三五个阶段下来，一个「简单的问题」要等十分钟到一小时。磁盘 I/O 像一道钝刀子，把「交互式分析」切成了「批处理作业」。工程师们甚至形成了独特的应对文化：提交查询，然后去开会、喝咖啡、改别的 bug——因为等待是确定会发生的。&lt;/p&gt;
&lt;p&gt;问题出在哪？不是 Hadoop 不行，而是 &lt;strong&gt;MapReduce 的设计目标本来就不是交互式查询&lt;/strong&gt;。它为容错而生：每个阶段的中间结果必须落盘，任何一台机器挂了，从磁盘上的中间结果重跑就行。这份「保险」在批处理场景物有所值，但对「我想马上知道答案」的分析师来说，是在为用不到的容错付全价。&lt;/p&gt;
&lt;p&gt;Presto 的研发初衷就一句话：&lt;strong&gt;为交互式 SQL 查询专门造一台引擎，PB 级数据，秒级响应。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;注意「专门」这个词。Presto 没有试图做一个「又能批处理、又能交互、又能点查」的全能选手——它从一开始就决定，为了快，该扔的就扔。下面是三次关键的「舍」。&lt;/p&gt;
&lt;h2 id="取舍一内存流水线扔掉容错换速度"&gt;取舍一：内存流水线——扔掉容错，换速度&lt;/h2&gt;
&lt;h3 id="舍task-级中间落盘与断点重试"&gt;舍：Task 级中间落盘与断点重试&lt;/h3&gt;
&lt;p&gt;MapReduce 的哲学是「每一步都存档」。Presto 反其道而行：&lt;strong&gt;查询被切成多个 Stage，Stage 内的算子组成流水线（Pipeline），数据以「页」（Page）为单位在算子间流动，全程驻留内存，中间结果不落盘。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;伪代码对比一下两种执行模型：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# MapReduce 式：每个阶段落盘存档
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;for stage in stages:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; results = stage.run(input)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; write_to_disk(results) # ← 每一步都付磁盘 I/O 的钱
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; checkpoint(results) # ← 为容错买单
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 下一阶段再 read_from_disk()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# Presto 式：内存流水线
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pipeline = build_pipeline(scan, filter, agg, ...)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;for page in source_pages: # 一页一页流过去
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; yield pipeline.process(page) # 算子间直接传递，不落地
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;差别有多直观？MapReduce 的一次「阶段交接」= 一次序列化 + 一次磁盘写 + 一次磁盘读 + 一次反序列化。Presto 的阶段交接 = 一次内存中的数据页传递。磁盘 I/O 从执行路径里被整个删掉了，这正是秒级响应的物理基础。&lt;/p&gt;</description></item></channel></rss>