跳转到内容
← 返回先驱
数据系统1943–约 17 分钟阅读

迈克尔·斯通布雷克

Michael Stonebraker

数据库IngresPostgreSQLVertica图灵奖

1970 年代中期,伯克利的 INGRES 与 IBM 的 System R 几乎同时证明:埃德加·科德 的关系模型可以被做成能跑查询的系统。迈克尔·斯通布雷克(Michael Stonebraker)没有发明关系代数,他把代数接进了优化器、存取路径与一门叫 QUEL 的查询语言。四十年后,他反复论证的却是另一句话:一种引擎无法同时成为最好的 OLTP、最好的数据仓库、最好的流处理与最好的科学数组库。

关于事务、隔离级别与日志,数据库与事务 已经写过。本篇不再把 ACID 清单重抄一遍。这里要看的是斯通布雷克怎样把查询处理做成可更换的体系结构,以及“一种尺寸不再适合全部”为何是一条关于存储布局与执行模型的定理式判断,而不是一句产品口号。

破除误解:他不是 SQL 之父,也不是 PostgreSQL 的日常维护者

第一层误解把 INGRES 写成 SQL 的起源。SQL 来自 System R 一侧的 SEQUEL。INGRES 用的是 QUEL。后来的市场选择了 SQL,不等于 1970 年代的伯克利系统在发明这门语言。斯通布雷克对 SQL 的语法长期不欣赏,认为它把语言设计与优化器需求缠在一起。评价贡献时,应看优化器与存储,而不是看谁的缩写赢了。

第二层误解把今天的 PostgreSQL 写成他仍在写代码的产品。Postgres(POST inGRES)是 1980 年代伯克利的对象关系数据库研究系统:用户定义类型、规则、早期的不覆盖存储。开源的 PostgreSQL 是这条代码树的后裔,经过了另一代社区的事务模型、MVCC 与扩展生态。PostgreSQL 的 MVCC 写的是后裔系统的机制。把每一个 Postgres 特性都记在他个人名下,会把研究原型与生产数据库混成一个专有名词。

第三层误解把 2014 年图灵奖读成“关系数据库的终身成就奖”。奖项文本强调的是他数十年把数据管理做成新的体系结构,而不是把 1970 年的关系模型再宣传一次。C-Store / Vertica 的列存、H-Store / VoltDB 的内存分区、SciDB 的数组模型,是这条奖词真正指向的后半段。

现场:INGRES、Postgres 与专用引擎

斯通布雷克 1943 年生,普林斯顿本科,1971 年在密歇根大学取得博士学位,随后长期在伯克利任教,后转 MIT。INGRES 的关键不是“会存表”,而是把查询当成可优化的代数表达式:选择下推、连接顺序、索引选择。数据库索引与查询优化 的课堂例子,许多直接来自这一代系统的实战。B 树与堆表如何配合,在 B 树与 LSM 树 里有更细的存储引擎对照;INGRES 当时面对的是磁盘上的行存与选择–投影–连接。

Postgres 把关系系统的类型系统打开。用户可以定义新的数据类型与运算符,优化器必须把这些运算符当成与内建类型同类的对象。规则系统试图把完整性、视图与推理放进同一套重写。部分想法被后裔系统以触发器、扩展与声明式约束吸收,部分被证明过重。研究原型的价值,常常是把一条路走到可以测量失败的地方。

2005 年前后的转折写在两篇会议论文里。ICDE 2005 的 One Size Fits All 论证:行存磁盘数据库在仓库负载上浪费 I/O,在 OLTP 上又被缓冲池与锁挡住。VLDB 2005 的 C-Store 把只读为主的分析做成列存、压缩与延迟物化。VLDB 2007 的 H-Store 把高频 OLTP 写成内存、分区、单线程串行执行:若分区后没有跨分区事务,锁与重做日志都可以大幅简化。大数据系统 后来的专用引擎浪潮,与这条论证是同一方向上的工业展开。

机制:布局决定算子,执行模型决定并发

行存与列存的差别首先是扫描的字节数。分析查询常只读少数列。行存把整行带进缓存,列存只读被引用的列,并让同一列的相邻值可压缩。延迟物化把连接与选择尽量留在压缩列上,直到投影真正需要拼出行。这不是“列更现代”,而是代价模型:当选择性低、列宽相对行宽很小,列存赢得的是内存带宽。反过来,点查询改一行、二级索引很多时,行存仍更自然。一种布局在两种负载上同时最优,需要代价函数自己打架。

H-Store 的机制是把并发从锁管理器里拿出来,放到分区边界上。每个分区一个单线程,事务若只碰一个分区,就按到达顺序执行,不必两阶段锁。跨分区事务变成少数昂贵例外。日志可以按命令记录而不是按物理页记录,因为内存是主本、磁盘是备份。崩溃恢复于是更像重放命令流。这条路的前提是分区键选得对。前提失败,系统就退回传统的锁与协调。斯通布雷克把前提写在论文标题附近,而不是写在脚注。

查询优化本身是组合搜索。连接顺序的搜索空间随表数阶乘增长,动态规划与启发式在可接受的时间内找“足够好”的计划。斯通布雷克一系系统的共同点是:把这个搜索当成体系结构的中心,而不是语法的附属。优化器选错计划,正确的存储也救不了延迟。反过来,存储布局一旦与计划假设不一致——例如优化器以为有聚簇索引而实际没有——代价模型就会系统性说谎。

INGRES 的选择下推与 System R 的 System R 风格动态规划,是同一代数上的两种搜索策略。选择下推把过滤尽量靠近扫描,减少后续连接的输入基数;连接顺序则决定中间结果的形状。两者都依赖统计:直方图、最常见值、空值比例。统计是谎言的有纪律版本——过期的统计仍是谎言,只是优化器会把它当成真的来乘。斯通布雷克后来批评通用引擎,部分原因正是:当存储从行变成列、从磁盘变成内存,同一套统计接口会继续输出对旧布局为真、对新布局为假的代价。

Postgres 的用户定义类型把这层再推进一步。新类型若不能把它的比较、哈希与选择率告诉优化器,就会被当成黑盒,计划质量立刻塌回顺序扫描。对象关系数据库的赌注因此不是“把 C 结构塞进表”,而是让扩展运算符进入同一套重写与代价系统。成功的后裔是 PostgreSQL 的扩展生态:PostGIS 一类类型把自己的索引与选择率接进去。失败的部分是规则系统:把视图、完整性与推理写成同一套重写,调试与语义都会失控。研究原型把这条路走到可测量,本身就是贡献。

C-Store 的延迟物化值得单独写清。列存若过早把列拼回行,压缩与向量化优势会在物化那一步被吐回去。正确的顺序是:在编码列上做选择与部分连接,直到投影清单真正需要的那些列才解码。压缩编码(游程、字典、位打包)于是不只是省空间,而是执行模型的一部分。Vertica 把论文做成产品时,真正难的是更新:列存擅长扫,不擅长改一个字段。后来的混合系统用 delta store 承接写入、再合并进列块,等于承认“一种布局”在同一引擎内部也撑不住读写两种代价。

Postgres 的存储决定把「时间」写进了元组,而不是事后打补丁。研究原型采用无覆写堆:更新写入新版本,旧版本留下身份,崩溃恢复与历史查询可以沿版本链走。今天 PostgreSQL 的 MVCC 已经演化成 xmin/xmax、快照隔离与 VACUUM 的完整账本,祖先观念仍在——读看到的是版本,不是「此刻唯一的那一行」。读不阻塞写,写不阻塞读,账单则是死元组必须被回收,否则表会在物理上膨胀。把多版本理解成锁的替代品,会漏掉这张账单;把 VACUUM 理解成可选项,会在生产里把账单变成磁盘事故。

进程模型同样是一条可测量的分叉。PostgreSQL 长期用每个连接一个操作系统进程,用共享内存协调缓冲池与锁:一个后端崩溃不必拖死整个服务,代价是进程创建与缓存布局都变成运维现实。VoltDB 走到另一极端,把事务收成分区上的单线程存储过程,用分区边界换掉通用锁管理器。两种模型都在回答同一问题:并发控制的复杂度应该放在内核锁上,还是放在编程模型与数据分布上。斯通布雷克的论文习惯把前提写在标题附近——分区键选得对,单线程才成立;前提失败,就退回锁与协调。

索引接口是可扩展性真正落地的地方。Postgres 把访问方法当成与类型同类的对象:B 树、散列、后来的 GiST/GIN,都可以为新类型注册。B 树与 LSM 树 对照的是写放大与读放大如何随布局变化;Postgres 的要点是:新类型不必等内核发版才能被索引,只要它带着合法的比较与一致的排序走过来。这解释了地理对象、全文检索与 JSON 为何能长在同一棵代码树上,而不必另起互不通信的引擎。类型、运算符、访问方法三者一起注册,优化器才有合法箭头去做选择下推与索引扫描。缺任何一项,可扩展只是把字节塞进 bytea 的借口。

共享磁盘与无共享(shared-nothing)是另一条他 1986 年就写过的分界。无共享把数据按键切开,每个节点拥有自己的磁盘与缓冲池,横向扩展时不必为页级缓存一致性开会。代价是跨节点连接与分布式事务变贵,于是负载必须承认分区。后来的数据仓库、H-Store 与许多所谓「大数据」引擎,其实是把这条分界从论文里的示意图,做成了机房默认。通用行存可以继续假装数据是一张完整的表;一旦扫描跨过机器边界,假装的代价会以网络往返的形式出现在计划里。

流系统 Aurora/Borealis、StreamBase 与科学库 SciDB 重复同一方法:先测量瓶颈落在 I/O、锁还是解释开销,再为那个瓶颈单独做一种引擎。数组模型让科学计算按块切,而不是把网格数据硬塞进行存再做自连接。这些系统有的活成了产品,有的停在论文,但它们共同拒绝「先写 SQL 再祈祷优化器」。声明式接口仍然重要,因为它把应用与存储隔开;隔开之后,存储这一侧必须允许被替换,否则独立性只是一句空的数据独立性口号。

2014 年图灵奖

ACM 把 2014 年图灵奖授予斯通布雷克,表彰他对现代数据系统概念与实践的贡献。他同时是连续创业者:Illustra 被 Informix 收购,Vertica、VoltDB、Tamr 等把论文里的引擎送进市场。学术与公司之间的往返,让他的论文带有一种少见的语气——直接写现有系统的哪一部分在给定负载下已经算不过来。这种语气也带来敌人:被点名的“一种尺寸”系统,往往就是他前半生参与塑造的通用行存数据库。

代价与争议

专用引擎的代价是操作复杂性。企业必须同时运行 OLTP、仓库、流与治理目录,数据在边界上复制与漂移。大数据系统 的集成成本,有时会吃掉专用引擎在单项基准上赢到的数量级。反对者认为:硬件变便宜之后,通用系统“足够好”且可维护。斯通布雷克的回应是测量:若工作负载的代价主导项已经换了(内存、网络、压缩、锁),旧架构的常数不会自己消失。两边都需要基准而不是口号。

另一处争议是他对 SQL 与“现有产品已经够用”的公开批评。有时被读成推销下一家公司。把论文与招股书拆开读更公平:C-Store 与 H-Store 的测量可以被独立复现;产品是否兑现论文假设,是另一份账单。研究允许把假设写强,产品必须在假设失败时仍能服务。

第三处是开源治理。PostgreSQL 社区的成功超出任何单一实验室的控制。创始叙事若把社区写成实验室的延长,会忽略后来二十年的贡献者。正确的因果方向更接近:研究原型证明对象关系可运行,社区把它做成可部署的基础设施。

SciDB 把数组而不是关系当成一等模型,针对科学仪器的多维 gridded 数据。这是“一种尺寸”论证的极端形式:若访问模式是切块与线性代数,硬把数组拆成表再连接,是在用错误的代数付带宽税。反对者会说 SQL 加数组扩展已经够用。测量仍然是裁判:切片是否碰到无关分区、线性算子能否靠近数据。斯通布雷克把这类系统做成公司,是因为论文里的速度必须在别人的集群上可复现;这也让利益冲突成为必须写明的事实,而不是用沉默假装不存在。

Aurora 与 Borealis 把流当成另一类引擎:查询是持续的,数据是暂态的。窗口、乱序与回放取代了事务的提交点。这再次验证同一判据——主导代价换了,日志与缓冲池的旧假设就应被丢掉,而不是用触发器模拟流。后来的流处理系统未必沿用这些名字,但“专用执行模型”已经被写进教材。

跨域连接

  • 组合数学:连接顺序、索引交叉与物化视图选择都是组合搜索。优化器并不求解最坏情况的组合爆炸,而是在有限预算里找可接受的计划。斯通布雷克把这一搜索放进系统中心,等于承认数据库的运行时问题首先是组合,其次才是磁盘。列存与行存只是给同一搜索提供不同的代价数字。计划空间的形状不变,数字变了,赢家就会变。
  • 范畴论:关系代数之所以能被优化,是因为它是一套带等式的代数:选择与投影可交换、连接可结合,重写才有许可证。范畴语言把对象与态射的组合写成可交换的图,数据库优化器日常做的正是这类图上的等价重写。不必把 Postgres 说成范畴论软件,但“查询是可重写的态射组合”这一层,解释了为何类型与运算符必须被当成一等对象——否则重写没有合法箭头。
  • 信息不对称:优化器拥有统计,用户拥有意图,存储引擎拥有物理布局。三方各自看见不同的一层。代价模型是把不对称压成一张可比较的表。统计过期时,不对称变成系统性误导:用户以为声明了查询,系统执行的是另一份计划。解释计划(EXPLAIN)存在的理由,就是把隐藏的一侧暂时翻开。专用引擎等于拒绝用同一张表去掩盖所有不对称。
  • 国家能力:普查、税收、医疗与土地登记依赖可审计的数据基础设施。能力不只是“有一个库”,而是能否为不同法定负载选择不会在峰值下说错话的引擎,并在多年窗口里迁移。斯通布雷克的专用化论证在公共部门尤其硬:一种通用行存若在分析季报上跑不完,国家不是缺少硬件,而是用错了布局。采购若只认单一品牌,等于把体系结构选择从技术问题改写成制度惰性。

人物小记:把负载写成体系结构

斯通布雷克不是把关系模型解释得更优雅的人。他把负载的主导代价写成存储布局与执行模型,然后在实验室和公司里各建一次。INGRES 证明关系可运行,Postgres 证明类型可扩展,C-Store 与 H-Store 证明通用引擎的常数可以被专门化打掉。图灵奖把这几次重建收成一条职业。

若把轨迹压成一句判据:好的数据系统贡献,是让后来者无法再假装所有查询都在同一张代价表上竞争。 布局、并发与日志必须随负载更换。通用系统仍会存在,因为操作成本是真实代价;专用系统也会存在,因为带宽与锁同样是真实代价。他的工作是迫使这两条代价被分开记账。

参考文献

  • Stonebraker, M. et al. The Design and Implementation of INGRES. ACM Transactions on Database Systems 1(3), 1976, pp. 189–222.(INGRES)
  • Stonebraker, M. & Rowe, L. The Design of POSTGRES. SIGMOD, 1986, pp. 340–355.(Postgres 研究系统)
  • Stonebraker, M. & Çetintemel, U. “One Size Fits All”: An Idea Whose Time Has Come and Gone. ICDE, 2005, pp. 2–11.(专用引擎纲领)
  • Stonebraker, M. et al. C-Store: A Column-oriented DBMS. VLDB, 2005, pp. 553–564.(列存)
  • Stonebraker, M. et al. The End of an Architectural Era (It's Time for a Complete Rewrite). VLDB, 2007, pp. 1150–1160.(H-Store / 内存 OLTP)

延伸阅读

  • Hellerstein, J. & Stonebraker, M. (eds.) Readings in Database Systems. 4th ed., MIT Press, 2005.(“红皮书”,数据库系统的论文选读)