由亚马逊内部禁止使用SQL数据库引出的想法 - nelhage


和我一起工作的人往往会意识到我对数据库特别是对SQL数据库有意见。上周,我写了一篇Postgres调试故事,并在推特上发布了AWS禁止内部使用SQL数据库的政策,并在Twitter上进行了讨论和辩论。本文是为了将其中的更多内容写到一个我可以引用的地方。
我相信这里的大多数评论都适用于大多数SQL引擎,但是由于我熟悉这些引擎,因此本文的重点将主要放在MySQL和Postgres以及偶尔的SQLite上。它们之间还涵盖了绝大多数开源SQL引擎的用法。
这些意见来自多年使用和/或运行SQL和其他数据库(主要作为Web应用程序或Web API的后端)的观点。我的经验主要是在这类应用程序中,这些应用程序往往具有相对可预测的数据访问模式,并且关心低延迟,高吞吐量和某种程度的高可用性。
 
SQL数据库具有令人难以置信的存储引擎
SQL数据库中的存储引擎是负责实际管理磁盘上的数据,将其持久化到磁盘并从中读回的层。它们是数据库中所有其他功能的基础。在(非分布式)SQL数据库中,它们在数据库的事务功能中也起着主导作用,管理原子提交背后的大多数复杂性,并介导数据库的许多一致性和隔离性。当我们谈到“ ACID”时,这些功能在很大程度上是存储引擎的属性,或者至少植根于存储引擎的属性。
开源SQL数据库绝对具有世界上最好的存储引擎。它们通过适当的调整和维护,提供了出色的吞吐量和底层硬件的高利用率,同时提供了强大的持久性保证和事务语义。它们有不同的优点和缺点,但是,如果您要持久地将一些低级记录存储在磁盘上,那么用通用的方法很难比MySQL的InnoDB,PostgreSQL或SQLite的存储层做得更好。
实际上,这些存储引擎是如此之好,以至于它们是单独使用这些数据库的原因。即使使用琐碎的(id int primary key, data blob)键值模式,使用SQL数据库也是有意义的,只是为了访问底层存储层的性能和持久性。
 
不喜欢SQL几个方面
那些令人难以置信的存储引擎(我们可以使用的一些最优秀的系统工程)都隐藏在我认为非常令人沮丧的接口后面。让我们看看我对SQL不满意的地方。

  • 字符串连接是错误的

即使您确定始终对实际数据使用查询参数(也应该如此),但针对SQL数据库编写代码几乎总是最终会通过在某些时候将字符串粘贴在一起而构造查询字符串,充其量,这是笨拙且麻烦的,更糟的是,它容易出错。
对于应用程序的编程使用,我宁愿使用一个从头到尾结构化数据的API,例如GRPC架构甚至MongoDB的BSON。以编程方式使用以消耗,生产或分析,并消除了所有潜在的错误,这要容易得多。
  • 不喜欢查询Planer

我不能否认查询计划者Planer非常有用,并且对于声明式查询执行的梦想有些不可思议的地方,您只需写下一个谓词,然后引擎“神奇地”使之高效。
但是,对于应用程序开发而言,绝大多数时候您都知道数据访问模式是什么,并且已经围绕它们设计了索引,并具有可预测性。对于具有一致的数据访问模式和高吞吐量的在线应用程序,性能是数据库接口的一部分;如果数据库继续提供查询服务,但延迟时间大大延长。
查询计划程序是可预测性的对立面。在大多数情况下,它会选择正确的,但是很难知道什么时候不会正确,或者如果不正确会产生什么样的影响。查询计划人员根据数据分布的估计来更改行为,因此即使EXPLAIN在CI时间运行也不足以提供保证。SQL引擎真的很难保证查询执行中的可预测性能。
尤其是Postgres顽固地拒绝执行强制选择索引的任何杂注,这令我非常恼火。这不是他们的计划者是否足够聪明的问题(尽管我遇到过犯严重错误的情况!),而是关于开发和数据管理的思维方式的问题。在某些时候,透明度,明确性和可预测性是非常重要的价值。
非常相似的查询将执行不同的查询,具体取决于存在的索引和计划者选择的索引,这很难预测。从
随着查询变得越来越复杂,查询计划程序问题在许多方面都变得越来越严重。
  • 不喜欢类型系统

SQL类型系统来自另一个时代。我宁愿选择像protobuf的类型系统那样的东西,该系统几乎完全专注于描述磁盘上的表示形式,并允许用户定义将语义分层的结构。总的来说,我认为在“存储类型”和“语义类型”之间更强的分离可能是一个富有成效的步骤。
而且,有这么多粗糙的边缘和怪异的角落:为什么Postgres有bytea其他数据库都有blob?为什么bigint在大多数引擎中都是64位整数,即使该词在大多数编程语言中表示“任意精度整数”也是如此?
这些并不是很大的问题,但是就造成精神开销和引擎与应用程序之间的阻抗不匹配而言,它们不是强制性的错误。
 
SQL是一种不错的ad-hoc查询和报告语言
鉴于对SQL的所有负面评价,SQL是一种相当不错的即席查询语言或探索性工具。如果我有一个很大的数据集要进行交互浏览,我的第一步通常是将其导入到SQLite中以进行交互操作,因为以一种简洁明了的方式来询问各种不同的问题非常容易。
对于交互使用的SQL,有很多技巧可以选择,但是在我看来,这在网络上确实是一种语言。
这种优势在某种程度上也可以延续探索性发展;如果您要构建一个快速迭代的早期应用程序,则可以使用SQL并使用查询计划器来编写当前所需的任何查询,然后在以后使它们变得高效-甚至以后,对于该功能-确实有价值。
 
迁移可能是我使用SQL数据库最大的麻烦
SQL数据库具有命令性的架构定义(CREATE TABLE …然后是ALTER TABLE …或CREATE INDEX…)。在我看来,绝大多数模式定义都应该是声明性的:您只需告诉数据库该模式应该是什么,它会对当前模式进行内省,然后将其演化为新的模式。如果无法自动执行此操作,则会引发错误,您需要在架构中添加其他编译指示或元数据以告知执行操作。
大多数ORM都以某种方式采用了这种方法,这就是为什么它对我感觉如此正确的部分原因。令我感到沮丧的是,SQL数据库本身无法以这种方式工作,而我们所有人都一次又一次地重新发明轮子,或者仅仅依靠重量级的ORM和应用程序框架来使我们的数据库可用。SQL数据库已经非常复杂,并且为它们的声明性查询语言感到骄傲。
许多ORM和框架都很好地解决了这个问题,但是当从头开始一个新项目时,这确实是烦人的设计选择和样板的真正根源。我认为这里的启动成本以及迭代的痛苦–编写和运行迁移操作只是为了向对象添加新字段,感觉非常沉重!
我认为这里的一个主要因素是,SQL引擎就像是一个时代的遗物,该时代做了更多的“前期大设计”。如果您在实施系统之前进行了大部分设计,则可以进行相当繁重的迁移过程。但是,现在我们强调紧密的迭代和敏捷演进,而我不得不编写并运行迁移只是为了有效地向应用程序中添加新字段,这让我感到非常恼火。
 
建议使用哪种技术进行数据存储?
细节在于魔鬼,但对我而言,默认情况下,尽管存在所有这些烦恼,我还是从MySQL开始。
这个答案常常使人们感到惊讶,因为MySQL的起步阶段很糟糕。然而,如今,带有InnoDB的MySQL2个作为一种存储引擎,它是我们拥有经过最严格测试和精心设计的软件之一。它已经由某些大型科技公司(包括Facebook和Google)以全球规模运行(并且在某些情况下仍处于运行状态)。反过来,他们投入了大量的工程工作来增强它的性能以及大规模的稳定性和可操作性。如今,MySQL甚至具有通过Vites基本上可用的现成的群集功能。
显然,MySQL遭受以上所有抱怨。仍然必须谨慎使用它,如果我要针对它开发应用程序,我将尝试尽可能地使用尽可能少的功能集。但归根结底,它的成熟性和灵活性将使我胜过大多数更新或趋势更强的NoSQL引擎。
至于Postgres,我对它以及它的工程和功能都非常敬重,但是对我来说,它在操作上太可怕了。在我的经验,这是很多比MySQL更糟糕的运营footguns和性能的悬崖,在使用它稍有不妥就可以完全破坏的性能或可用性。
另外,根据我的经验,由于MySQL的部署范围更广,因此更容易找到和雇用具有部署和操作经验的工程师。Postgres是一个不错的选择,特别是如果您已经在团队中使用过Postgres ,但是我个人已经被烧死了很多次。
更多点击标题原文。