是时候承认我们早就知道的一个事实了:NoSQL 是并不适合许多现代应用使用场景的工具,是我们该翻篇的时候了。

  由于当时的数据库无力处理所需的规模,NoSQL 技术应运而生。这种新一代数据服务的兴起解决了十多年前它问世时互联网规模和数据迅速增加带来的许多问题。NoSQL 还为冷存储/偶尔批量访问 PB 级数据提供了一条经济高效的新途径。然而,因急于解决大数据和大量并发用户带来的难题,NoSQL 丢弃了数据库的一些核心功能,而这些功能使得数据库拥有高性能和易于使用的优点。

  进行这番取舍也许是 NoSQL 为数据库领域做出的最大贡献。NoSQL 掀起了一场变革,集最佳的大数据功能与成熟关系模型的结构和灵活性于一体,推出了一种易于扩展的关系数据库。

  关系数据库不断发展,打造了全新一代的系统,可处理几乎所有的工作负载,满足现代应用所需要的可扩展性、可靠性和可用性等要求。传统的工作负载(比如事务应用和业务分析)转向比较新的工作负载(比如多租户服务和操作分析)。Google Spanner、Azure Data Warehouse 和 MemSQL,这些新的数据库大行其道证明了这点:对于大多数使用场景而言,关系数据库比 NoSQL 系统更易于使用,性能通常更胜一筹。

  我知道这可能会引起争议,也知道你可能立马觉得我的观点有偏见。不过容我仔细介绍一下这种数据库的历史、架构和应用,之后你自行判断也不迟。

  NoSQL 的崛起

  NoSQL 在 2000 年代末大放异彩,不过它很早就问世了。它的出现主要是为了解决现有数据库系统的规模问题。很显然,横向扩展(scale out)对于构建大型系统而言是一种更经济高效的模式。对于谷歌、Facebook、微软和雅虎构建的超大电子邮件和搜索系统而言,这是扩展规模的唯一方式。

  2007 年我读了 James Hamilton 介绍设计和部署大规模互联网服务的一篇文章(https://www.usenix.org/legacy/event/lisa07/tech/full_papers/hamilton/hamilton_html/index.html)后,首次认识到了横向扩展的价值。先是扩展应用层,因为无状态系统扩展起来比较容易。扩展存储层是另一回事。根据定义,数据库是有状态的,跨分布式系维护该状态的保证机制(即 ACID)非常困难。于是在现有数据库系统(比如 MySQL 和 SQL Server 等)的上面构建层,以创建一个分布式存储层。

  我在微软的 SQL Server 团队担任产品经理期间碰到过这方面的几个例子。第一个例子出现在微软内部:微软构建了 Webstore,这是 Hotmail 及相关服务使用的 SQL Server 上面的分片层。实际上,Webstore 是构建最终成为如今的 Azure SQL Database 的数据库系统的动因。虽然 Webstore 笨拙,缺少许多核心功能,但它很管用,让微软既能够针对所需的数据规模来扩展,又能够获得高可用性。但 Webstore 需要整个工程师团队来构建和维护。

  2000 年代中期,MySpace 使用大量的 SQL Server 服务器来管理这个迅速壮大的网站。该公司的用户增长非常快,每天需要增加新的 SQL Server 机器。运行所有这些 SQL Server、并且跨这些系统进行查询是一项非常复杂的工作,需要大批工程师来维护。

  同样的情况出现在了 Facebook 及其他公司,因为所有新兴的科技巨头都面临扩展难题。

  很显然,由于用户众多、数据不断增加,这些新的数字服务巨头需要一种新的解决方案来获取、管理和发掘数据。理想情况下,我们需要这样的系统:可直接提供单一接口,但又能横向扩展到许多机器上,并拥有内置的高可用性。

  最终,大规模云服务(谷歌、Facebook、雅虎和微软等)都自行构建了定制的系统,以满足规模扩展需求。那些系统各不相同,但采用了同样的基本思路,有的直接共享,而有的通过学术共享。最终,采用这些同样思路的开源系统开始涌现出来,NoSQL 浪潮方兴未艾。

  为了解决互联网规模问题,NoSQL 在几个关键方面有悖于传统数据库。接下来让我们看看为什么做出这些选择。

  最终一致性的性能和弊端

  存储系统有两种模型:ACID 和 BASE。

  ACID 代表原子性(Atomic)、一致性(Consistent)、隔离性(Isolation)和持久性(Durable)。它涵盖了你从大多数关系数据库获得的保证。ACID 保证写入操作必须等数据进入磁盘后才能向客户端返回成功讯号。此外,如果你很在意持久性(即不丢失数据),你可以对数据库进行配置,以便等到写入操作通过网路传输到另外某台机器,数据同样进入该机器的磁盘。因此保证了写入数据的正确性,但降低了写入速度方面的性能。

  BASE 是 NoSQL 系统所特有的,代表基本可用(Basically Available)、软状态(Soft State)和最终一致(Eventually Consistent)。由于应用程序不必等待查看写入是否持久化,写入时可更快地确保最终一致性。一旦数据存储系统收到写入操作,但在持久化到磁盘或另一个机器之前,它会告知应用程序写入操作成功,应用程序可以进入到下一个操作。因此你获得了性能方面的优势,但面临的风险是无法看到刚写入的数据,或者数据在出错情况下可能完全丢失。

  最终一致性合理兼顾了持久性与可用性。如果贵公司与消费者互动,延迟对贵公司的收入又有直接影响(所有内容、社区和商业应用环境都面临这个问题),你希望用户界面(UI)有最快的响应速度。如果你要扩大规模以支持数百万的并发用户,就无法容忍任何瓶颈。数据库架构中采用最终一致性带来的缺点是偶尔丢失某人的帖子或评论,而这种风险对于这些类型的应用而言是可以接受的。

  需要兼顾持久性与可用性的另一个例子是金融应用。你不希望银行使用最终一致性来存储 ATM 交易或股票销售的结果。在这种情况下,用户仍要求延迟基本为零,而银行又不愿意接受未写入到磁盘的交易。

  最终一致性有一席之地,但并非始终是唯一的解决方案。数据系统的架构师和开发员应能够选择自己想要哪种级别的一致性。应在使用环境层面而不是在平台层面进行这种选择。

  走无模式道路

  目前不清楚为什么 NoSQL 潮流中不见数据库模式(schema)的影子。是的,早期很难构建一个分布式元数据管理器以便跨分布式系统来维护模式以支持操作,比如添加列。因此,早期设计没有模式不足为奇。但最终完全消除了模式,而不是后来设法添加模式。有人认为模式会降低敏捷性,这也可以理解。好的模式设计很困难,需要事先认真思考。情况迅速变化时,你不希望被模式所束缚。

  但这是一个谬论。

  诚然,没有模式为负责将数据录入到系统的工程师增强了敏捷性。然而,它把这个问题推给了数据的读取者(即用户),而用户的数量通常高出一个数量级,而且数据写入时常常不了解数据的状态。这些用户通常从数据中创造价值,因此应面临尽量少的障碍。

  打个比方,设想一下图书馆声称废除杜威十进分类法,只是把书扔到地上的一个大洞里,声称这是一种更好的分类法,因为图书管理员要做的工作量少得多。半结构化数据有时间和地点属性,因为有时你事先不知道一些数据的结构,或者它是否稀疏。但如果你真的不知道任何进来的数据或数据是什么样子,那么数据又有何啥用?

  事实上,模式总是存在。数据对某人来说始终有意义。此人应该花时间将该数据编码到一个平台上,以便下一个人可以使用。如果数据混合已了解的数据和迅速变化的数据,那么将后者放到数据库中的半结构化列,然后搞清楚以后从中映射哪些列。15 年前,SQL Server 和 Oracle 可以对 XML 执行这项任务。MemSQL 及其他许多现代数据库现在可以对 JSON 数据执行这项任务。文档数据存储(以及键/值)应该是现代数据库的一项特性,而不是产品的唯一功能。

  面向查询的非 SQL 语法

  NoSQL 数据库设计中的这个决定遵循了无模式化原则。如果你没有模式,那么丢弃 SQL 语法还算合理。此外,很难为单单一种设备构建查询处理器,而构建分布式查询处理器难得多。尤其是,如果你是开发员,想让一个新的应用程序启动并运行起来,这种系统让人觉得更容易。

  MongoDB 在简单安装和首次体验方面做得堪称完美。但结果证明,关系模型非常强大。如果你根本不想回答“获取 id 是 2 的对象”之外的任何问题,单单有 get 和 put 函数就行。但是外头大多数应用程序到头来需要的不止如此。这篇文章解释了文档数据库的不足之处。

  在稍复杂一点的任何系统,你总是希望以不同于存储数据的方式来查询数据。具有讽刺意味的是,20 世纪 60 年代发明了关系模型,就是为了解那个年代的数据存储系统(IMS 和 Codasyl)存在的这个问题。拥有连接(join)功能的关系数据库是取出数据的唯一合理方式。是的,一开始比较难,但比将所有数据都获取到你的应用程序中、自行创建连接容易得多。我看到客户一次次地对 NoSQL 这么做,结果总是让人抓狂。

  许多这些 NoSQL 系统实现了主要目标。它们提供了单一接口的数据存储系统,可以横向扩展到许多机器上,拥有内置高可用性。虽然已取得了一定的成功,但 NoSQL 的采用还是遇到了阻碍。

  这有几个原因。性能是关键因素,尤其是在有服务级别协议(SLA)的情况下进行分析查询时。可管理性是另一个原因,因为分布式系统管理起来特别难。但是阻碍 NoSQL 采用的最主要因素还是需要对人员重新培训。许多人原先接受的是关系数据库方面的培训和教育。在过去这 10 年,NoSQL 一直试图让关系数据库人员改弦易辙,但收效甚微。所有 NoSQL 公司在产值 500 亿美元的数据库总共也仅占一小部分的市场份额。

  虽然软件工程师似乎喜欢 NoSQL,但数据人员(数据库管理员、数据架构师和分析员)老大不情愿地进入 NoSQL 领域;想实现必要的规模,NoSQL 又似乎是唯一的途径。而这意味着他们要重新学习新的 API、工具和生态系统,扔掉多年来积累的成功方法、模式和资产。他们希望使用一种成熟的模型来做事情,希望在不影响系统持久性、可用性和可靠性的情况下仍可以扩大规模。

  从 NoSQL 到 NewSQL ——确保性能和规模,又没有弊端

  我们构建 MemSQL 时,假设客户喜欢关系数据库的功能,又想要横向扩展型系统的可用性和可靠性。我们的目标是让客户可以两全其美。

  MemSQL 是一种支持事务和分析的分布式关系数据库,可在商用硬件上横向扩展。你可以获得熟悉的关系模型、SQL 查询语法和庞大的工具生态系统,以及现代云原生系统的扩展性和可用性。

  不妨对照一下 NoSQL 系统的核心差异。

  兼顾一致性和性能

  MemSQL 有一些旋钮(knob),让你可以在一致和性能之间进行适当的兼顾。这种取舍始终不可避免,但如今你不必在平台层面在这两者进行选择。你可以为合适的每个使用场景来进行选择。

  一致性 vs 性能并不是某个棘手的哲学命题,关键是哪个对你的应用和需求更重要。MemSQL 有两个设置可以让你对此进行调整。第一个设置让你可以决定是否等待磁盘持久化。有一个内存中缓冲区,可以在事务被持久化到磁盘之前存储事务。可以在数据一进入缓冲区就返回成功讯号,也可以在数据进入磁盘后返回成功讯号。如果进入缓冲区就返回,可能会在持久化之前出现机器故障或重启,数据就会丢失。另一方面,等待数据持久化到磁盘要花更长的时间。

  此外,如果是高可用性环境,有两种复制模式:同步复制和异步复制,确保数据在另一台机器上有第二个副本。如果你将复制设置为同步模式,你要等到辅助机器上收到事务后,才能将成功讯号返回给客户端。如果使用异步复制模式,事务返回成功讯号之后,数据复制到辅助机器。这让你能够调整一致性和持久性,以获得适合你风险/性能具体要求的性能。

  保持分布式系统中的模式

  MemSQL 实现模式的方式是,将元数据存储在小型内部数据库中,元数据更改时就将元数据同步复制到所有节点。它使用两阶段提交来确保 DDL 更改在集群中正确地传播,以一种不会阻塞选择性查询的方式来构建。

  不过 MemSQL 不仅仅支持关系模型。你可以输入一个列作为 JSON 列,并存储一个 JSON 文档。如果你觉得以后想要查询几个列,可以将属性映射为列,并编制索引。MemSQL 还支持空间类型和全文索引。我们明白,客户需要在一个熟悉的系统中有混合类型的数据,所有类型的数据都能自然地共存。

  保留 SQL“通用语”

  MemSQL 解决了在大规模环境下跨分布式数据库使用 SQL 语法的问题。分布式查询处理器让你可以使用标准 SQL 语法来表达查询,系统负责将查询任务分配到集群中的各节点,并帮你汇总结果。MemSQL 支持所有常见的 ANSI SQL 操作符和函数,它们为你提供了可表达几乎任何查询的强大模型。

  MemSQL 通过系统中两种类型的节点:汇聚器(aggregator)和叶子(leaf)来做到这一点。汇聚器节点处理分布式系统的元数据、路由查询和聚合结果。叶子节点存储数据,并处理在分区上执行查询这项繁重任务。如果可以,MemSQL 会在本地执行连接,这表明了为何模式设计相当重要。如果不行,MemSQL 将根据需要转移数据。因此,客户可以在不知道数据在底层如何分区的情况下使用 SQL 语言。

  这意味着借助 MemSQL,你可以利用贵公司已有的技能、资源和工具,或者人们可以像使用其他关系数据库那样使用 MemSQL,不需要重新培训。此外,由于 MemSQL 支持 MySQL 连线协议,现有的庞大生态系统(包括 BI、ETL 及其他中间件工具)完全与 MemSQL 兼容。你没必要雇用新的员工、学习一堆新工具或者引进新软件。只管用就行。

  向 NoSQL 说再见!

  由于 Web 应用和多租户服务大行其道,NoSQL 应运而生,以满足规模需求。想想解决这些问题的难度,就可以理解早期试图在存储层处理规模扩展的举措为何迫使用户作出艰难的取舍。

  但关系数据库迎来了发展。它们可以处理几乎所有的工作负载,满足现代应用程序所需的可扩展性、可靠性和可用性等要求。

  由于所有公司意识到数据驱动的价值,它们希望所有员工都能获得最新的数据。为此,需要一种新的分析系统,可以扩展规模以支持成百上千的并发查询、不需要预先聚合就可以快速查询,并且在数据创建时实时获取数据。除此之外,它们希望向客户和合作伙伴敞开数据,这需要切实可行的 SLA、安全功能、性能和规模,而目前的数据存储系统却满足不了这个要求。几种新的工作负载促使企业需要遗留数据库和 NoSQL 系统无力提供的新功能,操作分析之类的工作负载只是其中之一。

  关系模型经受住了时间的考验,它在不断添加新的创新。此外,它吸纳了新的数据类型 (搜索、空间和半结构化数据等)和一致性模型,那样各种数据就可以在一个系统中共存。关系模型或 SQL 查询语法没有固有的可扩展性难题。它只要不同的存储实现方法,就可以充分利用横向扩展型架构。

  MemSQL 等新的数据库已证明,对于大多数使用场景而言,关系数据库更容易使用,通常性能比 NoSQL 系统更胜一筹。

  谢谢你,NoSQL。你对数据库社区施加了压力,迫使社区解决云规模领域的难题。NoSQL 很管用。然而,关系数据库取得了发展,可以满足那些要求。我们已进入到下一个阶段。