在长达三十年的时间里,数据领域一直奉行一套核心思路:数据存放数据表,由数据库而非开发者来强制执行数据约束。谷歌的Bigtable主动舍弃了这些约束,因为基于这套规则构建的系统,根本无法承载整个互联网的海量数据。随后,掀起了名为NoSQL的技术浪潮,而这股浪潮后来又发生了部分反转。这场论战的核心,从来无关软件本身,而是一个根本问题:谁有资格为换取系统的扩容能力而牺牲数据正确性?
你使用的每一款应用,它的各类业务承诺:账户余额、成绩、消息记录,全都保存在数据库里。
当数据一致性保障遇上业务规模瓶颈会发生什么?
系统只能在“数据正确”和“服务可用”之间做取舍,很难两全,而必须有人做出这个抉择。
1970年,当时业界最前沿的数据库系统乱象丛生,Edgar F. Codd可以证明这一点。
Codd是IBM圣何塞实验室的一名数学家。二战期间,他曾在英国皇家空军服役,为一些最早的计算机编写过程序,后来则研究企业如何存储记录。后来,他发现了一个现象,并为此非常担忧。
当时领先的系统,包括IBM自家的IMS,它最初是为了追踪阿波罗登月计划里数百万个零件构建的,都是把数据存成一堆互相缠绕的记录,乱成一团。程序员要想找点东西,都得先把这团乱麻背得滚瓜烂熟。更糟的是,这团乱麻并不是背后的实现细节,就是数据本身。只要动一下数据的存储方式,所有读它的程序就全完了。
Codd给出的答案,是一篇标题干巴巴的论文:《大型共享数据库的关系数据模型》。它的想法朴素到了极点,也正因如此,才格外大胆:把所有东西都存进简单的行列表格;让程序去问数据“是什么”,而永远不问它“在哪儿”;把规则的维护从程序里拿出来,交给数据库自己去管。
IBM却没有采纳。IMS就是台赚钱机器,而Codd的模型则是动了它的奶酪。这个理念在IBM内部举步维艰,但在外部却迅速传播开来。
当时还是年轻创业者的Larry Ellison,读到IBM公开发表的论文,立刻嗅到商机,抢先把商用关系数据库做了出来。他干脆用产品给公司命名:Oracle。整个80到90年代,关系数据库和它的语言SQL几乎横扫整个行业,以至于“数据库”和“关系数据库”都快成了同一个词。
1981年,Codd获得图灵奖。2003年他去世时,讣告里称他的思想是现代世界的基石之一。
然而,现代业务规模的增长,这套体系逐渐不堪重负,而谷歌是第一个遇到这个困境的公司。2004年,谷歌要存储整个互联网的副本:数十亿网页、PB级海量数据,数据持续更新,每秒要处理成千上万次查询。按照关系型数据库的传统思路,解决方案就是采购性能更强、可靠性更高的服务器。但无论花多少钱,都不存在一台足够大的服务器可以扛住这样的负载。
谷歌应对硬件难题的思路截然相反:把计算任务拆分,分散到成千上万台廉价机器上,并且坦然接受机器会频繁故障的现实。在这样的硬件环境下,Codd提出的数据一致性保障机制会带来极高的性能开销。当多张数据表存放于同一台服务器时,表连接操作很快,但如果数据行散落在分布于三个数据中心的上千台机器上,同样的表连接就会产生海量网络流量,只要任意一台机器出现卡顿,整个查询就会陷入停滞。
于是,谷歌的工程师团队——Fay Chang、Jeff Dean、Sanjay Ghemawat以及其他同事,刻意打破了这套原有数据库的设计约定,开发出了Bigtable。它没有SQL接口,不支持表连接,也无法实现跨行事务。
本质上,它就是一张庞大有序的键值对列表,被切分成多段,分散存储在多台机器之上。只要通过主键查询某一行数据,无论表里是一百万行还是上千亿行,Bigtable都可以快速返回结果。可一旦提出更复杂的查询需求,就只能交给应用开发者自己处理。
这套系统效果显著。Bigtable支撑了谷歌网页搜索、地图与分析业务。2006年,谷歌做出一个不同寻常的举动:公开发表论文,对外披露Bigtable的整套设计。
一年之后,亚马逊发布了Dynamo,这同样是一套打破传统规则的存储系统。它的设计目标是保证购物车服务永远不会拒绝用户访问,哪怕这意味着购物车会同时存在多份版本,后续再做数据合并。
这些论文发表时,正值行业变革的风口浪尖。短短数月间,HBase、Cassandra、MongoDB等开源实现相继问世。到2009年,这股浪潮有了一个名字:NoSQL,并且风靡一时。Schema被视为累赘,JOIN更是老旧时代的产物。不少初创公司实际流量还不及谷歌的千分之一,就匆匆弃用关系型数据库,只为去适配一套自己永远都未必会达到的业务规模。
并非所有人都看好这股风潮。Michael Stonebraker,早在20世纪70年代就参与打造了最早一批关系型数据库,算得上是Codd思想最重要的继承者。他看着这场行业热潮,十分不解。他认为,整个行业在70年代付出惨痛代价,才真正理解数据库规则存在的意义,如今却主动将这些经验抛之脑后。
那场争论的结果本身并不重要,重要的是背后的问题:谷歌是真的遇到了现实的性能瓶颈,才审慎地做出取舍。但其它成千上万的企业,在并没有面临同类困境的情况下,也照搬了同样的妥协方案。Codd定下的这套数据保障准则,究竟是否可以舍弃?如果可以,又该由谁来决定舍弃它?
Codd的模型与Bigtable的模型逻辑都很直观,易于理解。二者的核心差异,在于计算负担落在了哪里。
Bigtable这类存储系统,本质上是一张巨大的有序映射表:每行对应一个查询键,行内包含多个带名称的值。下面这15行Python代码就模拟出了它的基本形态:
store = {} def put(row_key, column, value): store.setdefault(row_key, {})[column] = valuedef get(row_key): return store.get(row_key, {})put("com.example/pageA", "contents", "A cooking blog...")put("com.example/pageA", "links_to", "com.recipes/pageB")put("com.recipes/pageB", "contents", "A recipe...")put("com.recipes/pageB", "language", "en")print(get("com.example/pageA"))
留意两点:
现在向存储发起一个更复杂的查询:哪些页面链接到pageB?关系型数据库只需要一行join语句就能完成,数据库会帮你完成关联查找。但Bigtable做不到。它只能按行主键检索数据,而“pageB”只是埋藏在其它行的值里面。如果想要快速得到这个结果,你必须额外构建并维护一份反向索引数据,使用反向键:
backlinks = {}
def put_link(from_page, to_page): put(from_page, "links_to", to_page) backlinks.setdefault(to_page, []).append(from_page)
这第二个函数,用四行代码就道出了整套取舍逻辑。查询速度很快,但代价是两套数据结构必须始终保持一致,而数据库不会帮你校验。如果两次更新中间发生宕机,两份数据就会悄悄出现不一致。Codd的模型不会出现这种情况,数据库会把关联变更作为单个事务执行。而在Bigtable里,这件事得由开发者自己处理。计算开销并没有消失,只是从数据库内部转移到了每一个调用它的应用程序中。
完整的权衡关系如下:
对关系型数据库和Bigtable式NoSQL数据库在最重要的问题上进行了比较。
| 对比项 | 关系型(Codd) | Bigtable风格(NoSQL) |
|---|
| | |
| | |
| | |
| | |
| | |
最后这一点值得单独拎出来讲。Dynamo这类系统提供的是最终一致性:一次更新完成后,不同机器上的数据短时间内可能不一致,系统会在后续自动完成数据调和。亚马逊的判断是:购物车偶尔显示已删除商品,要好过购物车直接打不开。对于购物车场景,大多数人认可这个取舍;但换到银行余额场景,几乎没人能接受。
争论的根源就在这里。
各方观点环环相扣,后一种观点都在回应前一种观点。
1、Stonebraker的警告:这些教训我们早已经吸取过
Stonebraker并不是说谷歌在自身业务场景下的做法是错的。他的观点是,整个行业把一个特殊场景下的解决方案当成了一场技术革命。20世纪60年代的数据库就缺少强制的数据规则,而关系型模型正是为了解决那段时期暴露出的种种问题才被创造出来的。
因此,对绝大多数企业而言,放弃关系型模型算不上技术创新,而是在重蹈过去的覆辙,最终要由数据受损的用户来承担代价。
这一论点最有力的佐证就是真实发生过的事故。当时最流行的NoSQL数据库MongoDB,其早期版本的默认配置不会等待确认写入操作真正落盘完成。虽然基准测试里速度飞快,但在生产环境下会悄无声息地丢失数据。很多团队是通过客户的投诉工单,才切身体会到 “最终一致性” 带来的问题:订单重复扣款,账户余额结果随查询服务器不同而发生变化。
整套论述的核心立足点是第三条前提:你并不是谷歌。而对立观点正是针对这条前提发起反驳,认为该前提无关紧要。
2、规模优先派的反驳:旧模型的时代前提已经不复存在
Bigtable和Dynamo的设计者们并不认为自己在摒弃70年代的技术经验。在他们看来,70年代的技术建立在一个前提假设之上:单台可靠服务器,但互联网时代已经证明这个假设不再成立。他们的观点是:这种取舍是客观数学规律带来的必然结果,如果刻意回避这一点,才是真正的工程失职。
最有力的回应恰恰在于它的谦逊。谷歌实际测试过自身的性能边界,亚马逊也针对“购物车数据过期”这一故障模式做过主动权衡,该场景下数据过期的代价很低。
但这套逻辑只能证明那些完成过实测的工程师做得合理,却无法解释成千上万直接照搬结论、没有做过任何评估的团队。如果一项取舍只是跟风赶潮流,那就不再是理性权衡,而是拿用户的数据来赌运气。
由此引出第三种立场:真正的问题并不出自某一种数据库模型,而是整个行业选用技术工具的方式。
挑选数据库就像挑选运动鞋,大家只看大厂在用什么。技术大会分享、求职简历、“互联网大规模架构” 这类噱头主导了选型,而非业务实际需求。
按照这种视角,数据库选型是一件关乎伦理的事,因为背后承载着他人的重要记录:处方、存款、证据、学业成绩。如果一个团队在用药记录系统上采用最终一致性,无论团队内部有没有明确讨论过,都相当于替患者做出了风险抉择。
该批判对前面两种观点同时提出质疑:各方都在争论哪一种取舍方案更正确,但真正值得警惕的是人们对待取舍的草率态度——当数据悄无声息丢失时,没人说得清当初是谁允许承担这份风险。
剧情最后发生反转。2012年,正是当初打破传统数据库范式的谷歌,发布了Spanner。这个数据库在大规模分布式场景下重新带回了SQL、数据表模式和事务机制。与此同时,谷歌也发表了一篇论文:《Spanner: Google’s Globally-Distributed Database》。
这篇论文里有一段令人深省的表述:
与其总是要在代码层面弥补事务能力缺失带来的各种问题,不如让程序员在遇到性能问题时,再去处理性能开销。
多年来,谷歌眼睁睁地看着自己的工程师们,笨拙地手动构建Bigtable放弃的那些功能,每个团队都得单独为Codd买单。
整个行业都从两个方向逐步走向融合。MongoDB增加了事务与schema校验,以间接方式补上了这些约束能力。传统关系型数据库则吸收了对手最拿手的特性:PostgreSQL新增了灵活的JSON字段,允许结构不规则的数据,存放在具备强约束保障的系统里。现在,给初创公司的主流选型建议已经回归保守:先用Postgres,直到你能证明它满足不了业务需求。
这就引出了一个更加尖锐的问题:Codd的规则,先是被做过严谨评估的团队舍弃。等到大家为这个舍弃的决定付出代价之后,又重新捡起来。而中间这些年产生的损失,大多由用户承担,他们甚至不知道有人做过这样的取舍。
如果软件行业就是这样摸索边界:打破规则、上线交付、统计损失、再把规则加回来。这算得上是扎实的工程经验积累吗?还是一场拿公众做对象的实验?如果属于后者,谁签署了这份知情同意书?
Chang, Fay, Jeffrey Dean, Sanjay Ghemawat 等. 2006. “Bigtable:用于结构化数据的分布式存储系统。” 第七届 USENIX 操作系统设计与实现研讨会 (OSDI '06) 会议论文集。
https ://research.google/pubs/pub27898/
Codd, Edgar F. 1970. “大型共享数据库的关系模型。” ACM 通讯13 (6): 377–87。Corbett, James C.、Jeffrey Dean、Michael Epstein 等人, 2012 年。“Spanner:谷歌的全球分布式数据库。” 第十届 USENIX 操作系统设计与实现研讨会 (OSDI '12) 会议录。
https ://research.google/pubs/pub39966/
DeCandia, Giuseppe, Deniz Hastorun, Madan Jampani 等. 2007. “Dynamo:亚马逊的高可用键值存储。” 第 21 届 ACM 操作系统原理研讨会 (SOSP '07) 会议录。
https : //www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf
Gilbert, Seth 和 Nancy Lynch. 2002. “Brewer 猜想与一致、可用、容错 Web 服务的可行性。” ACM SIGACT 新闻33 (2): 51–59。Markoff, John. 2003. Edgar f. Codd, 79岁逝世;数据库关键理论家。《纽约时报》,2003年4月23日。
https : //www.nytimes.com/2003/04/23/business/edgar-f-codd-79-dies-key-theorist-of-databases.html
Stonebraker, Michael. 2010. SQL数据库与NoSQL数据库。ACM 通讯,53(4),10–11。
https ://cacm.acm.org/opinion/sql-databases-v-nosql-databases/
作者丨Brendan Shea 编译丨dbaplus社群来源丨网址:https://brendanpshea.github.io/computing_concepts_python/v2/cases/cs09_sql_nosql.htmldbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn