<?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/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/</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/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/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><item><title>同一个「存数据」的需求，为什么长出了三种完全不同的数据库？</title><link>https://fxio.site/posts/ai/2026-08-04-database-three-philosophies/</link><pubDate>Tue, 04 Aug 2026 11:53:09 +0800</pubDate><guid>https://fxio.site/posts/ai/2026-08-04-database-three-philosophies/</guid><description>&lt;h1 id="同一个存数据的需求为什么长出了三种完全不同的数据库"&gt;同一个「存数据」的需求，为什么长出了三种完全不同的数据库？&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;PostgreSQL、Redis、DuckDB——三者都管自己叫「数据库」，但架构几乎没有一处相同。本文从一个问题出发：为什么「存数据」这一件事，需要三种截然不同的答案？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;先说个真实感受：新人问我「该用什么数据库」时，我最怕听到的是「就……存点数据」。因为「存数据」这个需求背后，至少藏着三个互相矛盾的目标：&lt;strong&gt;要快（延迟）、要对（一致性）、要能算（分析吞吐）&lt;/strong&gt;。这三个目标在物理层面就是打架的，所以业界长出了三种完全不同形态的数据库，各自押注一个目标。&lt;/p&gt;
&lt;p&gt;这篇拿 PostgreSQL（75K+ Star 里的常青树）、Redis（75K Star）、DuckDB（40K Star）当样本——它们恰好是三条路线的教科书级代表。&lt;/p&gt;
&lt;h2 id="三种数据库三种世界观"&gt;三种数据库，三种「世界观」&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;PostgreSQL&lt;/th&gt;
&lt;th&gt;Redis&lt;/th&gt;
&lt;th&gt;DuckDB&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;一句话定位&lt;/td&gt;
&lt;td&gt;数据的基础设施&lt;/td&gt;
&lt;td&gt;远程数据结构接口&lt;/td&gt;
&lt;td&gt;SQLite for Analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;押注的目标&lt;/td&gt;
&lt;td&gt;正确性 &amp;gt; 性能&lt;/td&gt;
&lt;td&gt;延迟 &amp;gt; 一切&lt;/td&gt;
&lt;td&gt;分析吞吐 + 零部署&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据在哪&lt;/td&gt;
&lt;td&gt;磁盘（缓冲进内存）&lt;/td&gt;
&lt;td&gt;内存&lt;/td&gt;
&lt;td&gt;就在文件里（CSV/Parquet/S3）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;并发模型&lt;/td&gt;
&lt;td&gt;多进程、MVCC 多版本&lt;/td&gt;
&lt;td&gt;单线程事件循环&lt;/td&gt;
&lt;td&gt;单写者、Morsel 并行读&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;典型场景&lt;/td&gt;
&lt;td&gt;金融、ERP、订单&lt;/td&gt;
&lt;td&gt;缓存、会话、排行榜、限流&lt;/td&gt;
&lt;td&gt;数据科学家本地跑 SQL 分析&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注意：这三行的每一列差异都不是「实现细节」，全是&lt;strong&gt;世界观分歧&lt;/strong&gt;。往下看。&lt;/p&gt;
&lt;h2 id="postgresql把正确放在所有选项的前面"&gt;PostgreSQL：把「正确」放在所有选项的前面&lt;/h2&gt;
&lt;p&gt;PostgreSQL 从 1986 年 Berkeley 的学术项目走到今天，38 年没死，靠的不是快——它从来不追求最快。它的护城河是&lt;strong&gt;正确性和可信度的复利&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;几个能看出价值观的设计：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Process-per-connection：每个连接一个独立进程。&lt;/strong&gt; MySQL（InnoDB）用线程池，PG 用进程。进程更重、启动更慢，但一个连接崩了不会带走别人——内存隔离是操作系统保证的，不是代码纪律保证的。这是「可靠性高于便利」的字面体现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. MVCC 的代价是 VACUUM。&lt;/strong&gt; 读写互不阻塞的代价是旧版本数据堆在表里，得靠 VACUUM 回收。很多人骂 VACUUM 烦，但这笔交易是明码标价的：读永远不阻塞写、写永远不阻塞读，换来的运维成本。对比 MySQL undo log 的回收方式，各有取舍，但 PG 把旧版本直接放主表的设计，让「时间旅行」「闪回查询」这类能力成为可能。&lt;/p&gt;</description></item></channel></rss>