首页 › programming › 它没免费午餐:数据库读写存必有一败

它没免费午餐:数据库读写存必有一败

导语

如果你写过任何一点数据库代码,多半撞过这堵墙:给表加了个索引,查询从 800 毫秒掉到 2 毫秒,爽;但写入开始变慢、磁盘还悄悄膨胀了一截。 你以为是自己索引建得不够好、参数调得不够精。其实不是——这是底层一条铁律在作祟,你越用力压一头,另一头就翘得越高。

2016 年,哈佛大学 DASlab 实验室的 Stratos Idreos 团队在 EDBT 会议上发了一篇论文,把数据库工程师几十年来"凭手感"拿捏的那种纠结,提炼成了一句近乎宿命论的断言:一个存取方法(access method),不可能同时把读、写、存三件事都做到最优。 你能给其中两件设上限,第三件就必然触底——不是"比较差",是数学结构上被钉死的下限。他们管它叫 RUM Conjecture(RUM 猜想):R=Read 读、U=Update 写改、M=Memory 存储。

这期由 Arpit Bhayani(DiceDB 作者、前 Google GCP Memorystore 工程师)主讲的 Understanding Database Trade-offs — The RUM Conjecture,就是把这篇论文嚼碎了讲给你听。它的价值不在于教你某条 SQL,而在于给你一副"数据库选型"的透视镜——从此你看到 Redis、Cassandra、RocksDB、Postgres,看到的不再是品牌,而是它们各自站在 RUM 三角形的哪个角上,以及为此牺牲了什么。

视频背景卡片

项目 内容
视频标题 Understanding Database Trade-offs - The RUM Conjecture (Paper Explained)
频道 Arpit Bhayani
频道订阅 约 21 万
视频类型 Paper Explained(顶会论文逐句拆解)
所属合集 Database Engineering 系列
核心主题 为什么没有任何一种数据结构能同时优化读、写、存
论文出处 EDBT 2016 Designing Access Methods: The RUM Conjecture

核心内容拆解

1. "理想的存储系统"在物理上就不存在

视频一上来就先立一个靶子:什么是理想的存取方法?答案是三全其美——读得最快、写得最省、还不占用任何额外内存。

听起来像废话,但只要你想清楚"快"从哪里来,就明白它不可能。读要快,就得有索引、有缓存、有冗余副本帮你秒定位;写要省,就得少动磁盘、批量攒着写、避免每次都重组数据;存要省,就绝不能搞副本、不能加索引、最好把数据压缩到极致。这三条优化方向,本质上是在互相拆台:你为了读快而建的索引,正是写入时要同步维护的负担,也正是吃掉内存的元凶。

RUM 猜想就是把这种"互相拆台"形式化:当你给读、写、存其中两件事都设了上限(让它俩别太差),第三件就被迫踩到一个再也降不下去的下限。注意措辞——是 conjecture(猜想),不是 theorem(定理)。它没有被严格证明,但被全世界无数系统反复观测到,和著名的 CAP 定理(一致性、可用性、分区容忍不能三全)是同一类的"工程铁律"。Arpit 在原文里直接点明:RUM 之于存储,就像 CAP 之于分布式系统。

2. 三个"放大系数":把模糊的"贵"变成可比的数字

光说"读得慢"太虚,论文给了三个可计算的指标,这是整期视频最该记的硬通货:

有了这三个比率,"Redis 比 Cassandra 快"这种含糊说法就能被拆解成:"Redis 在读放大上更优,Cassandra 在写放大上更优"。数据库之间的差异,本质是这三个比率的取舍差异——这是这期视频最值钱的一句潜台词。

3. 读优化派:B-Tree 与 Hash Index 的代价

第一类系统死磕读,把读放大压到极低。代表是 Hash Index(哈希索引,O(1) 定位) 和 B-Tree / B+Tree、SkipList(O(log n) 定位)。

它们为什么读得快?因为养了一支"辅助部队"——索引结构。但天下没有免费的索引:为了维护这支辅助部队,每次写入/更新主数据时,所有相关索引都要跟着改一遍,这就是 U(写)的代价;而这些索引本身要占内存和磁盘,这就是 M(存)的代价。B-Tree 还有个经典副作用叫页分裂:数据按顺序塞满一个页后,再插一条就要把页劈成两半、还要更新上层所有指针,单次写入可能牵动整条路径的重写。

所以读优化系统的命门很清楚——一旦写入变频繁,性能立刻跳水。这就是为什么纯读优化的设计扛不住高并发写入场景。Postgres、MySQL 的传统行存 B+Tree 索引、Redis 的字典,都属于这一族。

4. 写优化派:LSM-Tree 的精明与隐痛

第二类系统反过来死磕写,代表是 LSM-Tree(日志结构合并树),以及 Partitioned B-Tree、FD-Tree。大名鼎鼎的 RocksDB、LevelDB、Cassandra、HBase、InfluxDB,底层全是 LSM。

LSM 的思路很巧:写入根本不就地改老数据,而是追加到内存里的 MemTable,攒够了再批量、顺序地刷成磁盘上的只读文件(SSTable)。顺序写、批量写——这正是磁盘和 SSD 最喜欢的姿势,所以单次写入的延迟极低。

但 RUM 的报应立刻来了:读放大爆炸。你要查一条数据,它可能藏在第 0 层、第 1 层……一直到第 N 层的某个 SSTable 里,引擎得逐层翻找、再把多版本合并去重才能给你答案。空间放大也爆炸:同一条数据的新旧版本散落在不同层,在 compaction(后台压缩合并)把它们清理掉之前,磁盘上挤满了冗余。

更妙的是,LSM 标榜自己"写省",可后台那个为了压制读放大和空间放大而存在的 compaction,会把老数据反复重写好几遍——业界实测 LSM 的写放大经常是 10~30 倍甚至更高。你以为省了写,其实只是把写的代价推迟并放大到了后台。这正是 RUM 张力的完美现场:你压住了"即时写延迟",U 的总量却被 compaction 偷偷抬了起来。SSD 寿命(TBW)焦虑,很大一块就来自这里。

5. 存优化派:Bloom Filter 的"有损换内存"

第三类系统死磕存,把空间放大压到极致,思路是要么压缩、要么允许出错。代表是 Bloom Filter(布隆过滤器)、Count-Min Sketch、稀疏索引、各种有损编码。

这正好接上本系列之前那期 它故意撒谎:布隆过滤器永不漏报 —— 布隆过滤器就是 RUM 三角形里"存优化"那个角上的招牌居民。它用极少的比特(典型地,1% 误判率只需约 10 bit/元素)就能告诉你"这条数据八成不在",把昂贵的磁盘读拦在内存里。代价是什么?它会说谎(假阳性),还需要在写入时计算多个哈希、读出时多次校验——U 和 R 都被这层"概率判断"拖慢了。

Count-Min Sketch 同理:用固定的小内存做近似统计,牺牲精度换空间。这一派的核心哲学是:"够用就行,近似也是答案"。当你的业务能容忍一点点误差(缓存判断、热度排行、去重计数),存优化派就能帮你省下数量级的内存。

6. 为什么是"猜想"而不是"定理"——这很重要

视频特意强调了一个细节,也是很多人会误解的地方:RUM 是 conjecture,不是 theorem。

定理需要严格的数学证明,给出"不可能"的硬下界。而 RUM 目前没有被这样证明过——它是大量工程实践的归纳总结。论文作者自己也很诚实:对于某些特定的比较型数据结构,已经有读-写权衡的下界证明,但"读、写、存三者同时"的完整下界,至今是个开放问题。

那它还有用吗?太有用了。 工程世界里,一条"几乎总是成立、且代价一目了然"的经验法则,往往比一个严苛但狭窄的定理更可操作。CAP 也是同理——它不是数学定理的完整形式,却成了分布式系统设计的指南针。RUM 给你的不是"不许你做什么",而是"当你决定优化某一项时,请主动预判另外两项会怎么恶化",并去量它、补它。

7. 平衡点与终极理想:RUM Adaptive

如果三角形的三个角都各有死穴,那有没有"尽量站中间"的设计?有。视频举了 Block-based Clustered Indexing(块式聚簇索引):它只建一棵很矮的辅助树(少量指向数据页的指针),而且因为数据本身聚簇地存在索引里(主数据即索引),引擎查到就能直接拿到,不必再回主存储取一次——读放大很低,写和存也还算克制。它是 RUM 三角形里靠近中心的一个妥协点。

但真正的圣杯是论文提出的 RUM Adaptive(RUM 自适应)存取方法:一个系统能根据当时的负载、应用、硬件,动态调整自己在三角形上的位置——读多就偏读优化,写洪峰就偏写优化,内存吃紧就开压缩。Arpit 把它总结成一句漂亮的话:"应该是应用、负载和硬件来决定我们怎么访问数据,而不是我们系统的约束去反过来限制应用。"这其实也是当下数据库演进的方向:从"一种结构打天下",走向"可调的、自适应的"混合引擎。

知识结构图

graph TD
    RUM["🎯 RUM 猜想<br/>读 R · 写 U · 存 M<br/>三优不可兼得"]
    RUM -->|"读-优"| R["R 读优化<br/>低读放大"]
    RUM -->|"写-优"| U["U 写优化<br/>低即时写延迟"]
    RUM -->|"存-优"| M["M 存优化<br/>低空间放大"]

    R --> R1["Hash Index · B-Tree<br/>Postgres · MySQL · Redis"]
    U --> U1["LSM-Tree<br/>RocksDB · Cassandra · HBase"]
    M --> M1["Bloom Filter<br/>Count-Min Sketch · 稀疏索引"]

    R -.代价.-> RC["写放大↑ + 空间↑<br/>页分裂/索引同步"]
    U -.代价.-> UC["读放大↑ + 空间↑<br/>compaction 反噬写"]
    M -.代价.-> MC["读放大↑ + 写放大↑<br/>假阳性/解压开销"]

    BAL["⚖️ Block-based Clustered Indexing<br/>三角形中心 · 折中"]
    ADAPT["🧭 RUM Adaptive<br/>按负载动态调位 · 终极理想"]
    RUM --> BAL
    BAL --> ADAPT

实战启示

第一,选型前先问负载,别先问品牌。 拿到一个新系统,第一反应不是"它快不快",而是"它把 R/U/M 三者里的哪个压到了地板?又把哪个顶上了天?" 这样你才能预判它在你具体场景里会爆在哪。读多写少的用户中心、商品库 → 读优化(B-Tree 类);海量时序、日志、IM 消息流 → 写优化(LSM 类);内存吃紧、能容忍概率 → 上 Bloom Filter / Sketch。

第二,警惕"单索引治百病"的幻觉。 每加一个二级索引,你就在用 U(写入变慢)和 M(空间膨胀)去赎买 R(读变快)。索引越多不是越强,是债务越重。生产环境里"加个索引就好了"的提议,先算清楚它每天要被写入维护多少次、占多少 GB。

第三,真实系统都是"RUM 混合体",学会组合补刀。 这是最值钱的一条:没有任何顶级系统只站在一个角上。 RocksDB 是写优化的 LSM,但它会在每个 SSTable 上挂一个 Bloom Filter,用存优化的"小谎言"去把读放大从"翻遍所有层"砍回来——这就是在用 M 补 R。Redis 这种读优化的内存库,写多了也得靠 RDB/AOF 和淘汰策略去兜底。读不行就补 Bloom、写不行就上 LSM、存不够就压缩——三层思维,按需叠加,这才是工程师该有的工具箱,而不是迷信某一种结构。

第四,把三个放大系数挂上监控。 别等磁盘满了、SSD 写穿了才发现。读放大看 P99 延迟与回表次数,写放大看 SSD 写入量/用户实际写入量比值(云厂商的 IOPS 账单会诚实地告诉你 LSM compaction 有多狠),空间放大看实际占用/逻辑数据量。RUM 不是理论游戏,是直接对应你账单和故障的三个数。

延伸学习路径

  1. 原论文:Athanassoulis 等人,Designing Access Methods: The RUM Conjecture(EDBT 2016)——一切的源头,配套还有 SIGMOD 2016 的 Design Tradeoffs of Data Access Methods。
  2. Harvard DASlab RUM 项目页(daslab.seas.harvard.edu/rum-conjecture)——官方一手资料,含论文 slides 和后续 RUM-aware 研究。
  3. Arpit Bhayani 的 Database Engineering 播放列表——本视频所属合集,把 LSM、B-Tree、WAL 一个个掰开讲,是这个主题最好的中文友好(英文讲授)进阶路径。
  4. RocksDB / LevelDB 源码与 wiki——去真实代码里看 compaction、Bloom Filter、leveled vs tiered 是怎么把 RUM 三角形上的取舍"工程化"的。
  5. CMU 15-445 数据库系统课程(Andy Pavlo)——B+Tree、LSM、索引的系统性教材级讲解,看完 RUM 猜想再回头听课,会突然听懂很多以前觉得"就是规定"的设计选择。

一句话总结

没有任何数据结构能同时省下读、写、存——RUM 猜想把数据库选型从"比参数"提升为"量取舍",让你看懂每个系统站在三角形哪个角、又为此把哪一刀留给了你。

参考来源

  1. Understanding Database Trade-offs - The RUM Conjecture (Arpit Bhayani) — 本次分析的核心视频
  2. The RUM Conjecture — Harvard DASlab 官方项目页 — 论文出处与 RUM 原始定义的一手来源
  3. The RUM Conjecture - Storage System Trade-offs (Arpit Bhayani 博客) — 三类系统与放大系数公式的中文友好拆解
  4. Designing Access Methods: The RUM Conjecture (EDBT 2016, IBM Research) — 原始论文著录
  5. The RUM Conjecture: You Cannot Optimize Reads, Updates, and Memory at Once (ankit-rana.com) — 含 B-Tree / LSM 对照的延伸解读

本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入