下载本文的PDF版本 PDF

外部数据 vs 内部数据

保存在 SQL 外部的数据与保存在内部的数据具有不同的特性。

Pat Helland

最近,人们对服务产生了浓厚的兴趣。这些可以是微服务,也可以仅仅是服务。在每种情况下,服务都提供具有自身代码和数据的功能,并独立于合作伙伴运行。本文认为,封装在服务内部的数据与发送到服务边界外部空间的数据之间存在许多重要的差异。

SQL 数据封装在服务内部,以确保它受到应用程序代码的保护。当跨服务发送数据时,它就位于该信任边界之外。

本文提出的第一个问题是,信任对于服务及其封装的数据意味着什么。这可以通过查看事务和边界、保存在服务内部与外部的数据来解答。还需要考虑的是服务如何使用运算符(请求内容)和操作数(细化这些请求)进行组合。然后,本文探讨了时间和服务边界。当数据库中的数据解锁时,它会影响时间的概念。这引出了对消息、模式和数据在这些边界之间流动的服务组合中使用不变性的考察。

然后,本文着眼于这些信任边界(称为服务)外部的数据。当数据在一个在服务内部的世界中流动时,如何构建这些数据,使其在空间和时间上都具有意义?服务内部的数据又如何呢?它与传入和传出的内容有何关系?

最后,考虑了 SQL 和 JSON(JavaScript 对象表示法)以及其他半结构化表示的特性。它们的优势和劣势是什么?为什么解决方案似乎都使用它们来完成部分工作?

 

基本服务

服务对于当今构建大型应用程序至关重要。虽然有很多利用大型企业解决方案的例子,但行业仍在学习将服务作为一种设计范例。本节描述了术语服务的使用方式,并介绍了数据驻留在服务内部和服务外部的概念。

 

服务

大型和复杂的系统通常是独立和自治服务的集合。每个服务都由一块代码和数据组成,这些代码和数据是该服务私有的。服务不同于经典应用程序,后者生活在应用程序孤岛中,因为服务主要设计为通过消息传递与其他服务交互。事实上,这种交互、其数据以及它们如何协同工作是一个有趣的话题。

服务完全通过消息相互通信。除了消息格式和预期消息序列之外,不共享关于合作伙伴服务的知识。明确允许(并且确实预期)合作伙伴服务可以在堆栈的各个级别使用异构技术来实现,包括硬件、操作系统、数据库、中间件、编程语言和/或应用程序供应商或实施团队。

服务的本质在于其独立性以及它如何封装(和保护)其数据。

 

通过封装限定信任

服务与一系列消息进行交互,这些消息的格式(模式)和业务语义都得到了很好的定义。每个服务将仅根据明确定义的消息为其合作伙伴服务执行有限的操作。定义一组有限行为的行为为服务提供了坚实的封装。信任的一个重要部分是限制您为外部人员所做的事情。

要与服务交互,您必须遵循其规则和约束。您发送的每条消息都符合预定的角色。与另一个服务中的数据交互的唯一方法是通过其规则和业务逻辑。通常情况下,除非经过应用程序逻辑处理,否则数据永远不允许离开服务。

例如,当您使用银行的 ATM 时,您期望只有少数支持的操作,例如取款、存款等。银行不允许通过 ATM 直接访问数据库连接。更改银行数据库的唯一方法是通过 ATM 和后端系统中的银行应用程序逻辑。这就是服务保护其数据的方式。

 

封装更改和读取

服务使用其应用程序逻辑封装对其数据的更改。应用程序逻辑确保服务数据和工作的完整性。只有服务受信任的应用程序逻辑才能更改数据。

服务封装对其数据的读取访问。这控制了导出的内容的隐私。虽然这种自主性很强大,但也可能导致一些挑战。

在您的业务将工作划分为独立服务之前,其所有数据都位于一个大型数据库中。现在您有一堆服务,它们有一堆数据库在运行在一堆计算机上,这些计算机运行着一堆不同的操作系统。这对于不同服务的独立开发、支持和演进来说非常棒,但是当您想要跨所有数据进行分析时,这是一个巨大的麻烦。

通常,每个服务都会选择导出经过仔细清理的数据子集,供合作伙伴服务使用。当然,这需要一些工作来确保查看此数据的适当授权(以及验证好奇的服务)。尽管如此,清理和控制暴露的数据的能力至关重要。

 

信任和事务

参与 ACID(原子性、一致性、隔离性、持久性)事务意味着一个系统可能会被锁定,等待另一个系统决定提交还是中止事务。如果您被卡住,持有锁等待另一个系统,那真的会给您的可用性带来麻烦。除极少数情况外,服务不会像那样信任其他服务。

在 20 世纪 90 年代后期,人们努力制定跨信任边界的事务协调标准。幸运的是,这些标准惨遭失败。

 

服务内部和外部的数据

本文的前提是,驻留在服务内部的数据在许多本质上与驻留在服务外部或服务之间的数据不同

• 内部数据是指包含在服务本身内的封装的私有数据。笼统地说,这是始终被认为是“正常”的数据——至少在您大学的数据库课程中是这样。SQL 数据库中包含的经典数据以及由典型应用程序操作的数据都是内部数据。

• 外部数据是指在这些独立服务之间流动的信息。这包括消息、文件和事件。它不是您经典的 SQL 数据。

 

运算符和操作数

在服务之间传递的消息包含运算符,这些运算符对应于消息的预期用途。通常,运算符反映了服务领域中的业务功能。例如,实现银行应用程序的服务可能在其消息中包含用于存款、取款和其他银行功能的运算符。有时,运算符反映了发送消息的更平凡的原因,例如“这是星期二的价格表。”

消息可能包含操作数给运算符,如图 1 所示。操作数是运算符消息为完全限定消息意图所需的额外内容。操作数可以从参考数据中获得,参考数据发布用于描述这些操作数。从电子商务网站请求购买的消息可能包括产品 ID、要购买的请求数量、预期价格、客户 ID 等。稍后将对此进行更详细的介绍。

Data on the Outside vs. Data on the Inside

 

数据:过去和现在

本节考察了不跨服务共享 ACID 事务的时间影响,并考察了 ACID 事务边界内的工作性质。这为针对内部数据的操作提供了清晰的“现在”感。

然而,服务外部数据的情况有所不同。它被解锁的事实意味着数据不再处于现在。此外,运算符是对尚未发生的且实际上存在于未来的操作的请求(假设它们能够实现)。

不同的服务生活在它们自己的私有时间域中。这是使用不信任服务的内在组成部分。信任和时间对如何思考应用程序具有影响。

 

事务、内部数据和现在

事务在历史上一直是使用 ACID 属性定义的。1 这些属性反映了事务的语义。已经做了很多工作来描述事务可串行化,其中在系统或相关系统集上执行的事务感知到它们的工作以串行顺序应用,即使在并发执行的情况下也是如此。2 事务可串行化让您感觉孤单。可串行化的另一种说法是,每个事务都将所有其他事务视为以下三种类别之一

• 那些工作先于此事务的。

• 那些工作跟随此事务的。

• 那些工作与此事务完全独立的。

这看起来就像执行的事务完全孤立一样。

ACID 事务存在于现在。随着时间的推移和事务的提交,每个新事务都会感知到先前事务的影响。服务的执行逻辑生活在清晰而明确的现在感中。

 

来自过去的冲击

消息可能包含从本地服务数据库中提取的数据。发送应用程序逻辑可能会查看其内部以从其数据库中提取该数据。当消息离开服务时,该数据将被解锁。

目标服务看到消息;发送者服务上的数据可能会因后续事务而更改。不再知道它与发送消息时相同。消息的内容始终来自过去,而不是现在。

距离上不存在同步性。类似于光速限制信息,当您看到远处物体时,它可能已经发生了变化。同样,当您看到消息时,数据可能已经发生了变化。

服务、事务和锁限定同步性

• 在事务内部,事物是同步的。
• 同步性仅存在于事务内部。
• 同步性仅存在于服务内部。

从远程服务看到的所有数据都来自“过去”。当您看到来自远程服务的数据时,它已被解锁,并且可能会发生变化。每个服务都有自己的视角。其内部数据提供了其“现在”的框架。其外部数据提供了其“过去”的框架。我的内部不是你的内部,就像我的外部不是你的外部一样。

使用服务而不是单个集中式数据库就像从牛顿物理学转向爱因斯坦物理学
• 牛顿的时间以统一的速度向前推进,具有远距离的即时知识。
• 在服务之前,分布式计算努力使多个系统看起来像一个系统,使用 RPC(远程过程调用)、两阶段提交等。
• 在爱因斯坦的宇宙中,一切都相对于一个人的视角而言。
• 在每个服务中,都有一个内部的“现在”和消息中到达的“过去”。

 

对未来的希望

消息包含运算符,这些运算符定义来自服务的工作请求,如图 2 所示。如果服务 A 发送一条包含运算符请求给服务 B 的消息,则服务 A 希望服务 B 执行请求的操作。
换句话说,它对未来充满希望。如果服务 B 遵守并执行了工作,则该工作将成为服务 B 未来的一部分,并且其状态将永远改变。一旦服务 A 收到描述操作成功或失败的回复,服务 A 的未来就会发生改变。

Data on the Outside vs. Data on the Inside

 

“那时”的生活

操作数可能存在于过去或未来,具体取决于其使用模式。如果它们具有来自远程服务的解锁信息的副本,则它们存在于过去。如果它们包含提议的值,并且希望在运算符成功完成时使用,则它们存在于未来。

在服务之间,生活在“那时”的世界中。运算符存在于未来。操作数存在于过去或未来。当您在服务范围之外时,生活始终在“那时”。这意味着外部数据生活在“那时”的世界中。它是过去或未来,但不是现在。

每个单独的服务都有其自己单独的“现在”,如图 3 所示。事务可串行化的域是不相交的,并且每个域都有自己的时间环境。它们交互的唯一方式是通过外部数据,这些数据生活在“那时”的世界中。

Data on the Outside vs. Data on the Inside

 

处理现在和那时

服务必须应对使现在与那时相遇的问题。每个服务都生活在自己的现在中,并与传入和传出的“那时”概念进行交互。服务的应用程序逻辑必须协调这些概念。

例如,考虑一下企业接受订单时涉及的内容:企业可能会发布每日价格,但它可能希望在午夜后的一段时间内接受昨天的价格。因此,服务的应用程序逻辑必须手动处理重叠期间的价格差异。

同样,一家声称其产品“通常在 24 小时内发货”的企业必须考虑以下事项:订单处理具有旧信息;可用库存被故意模糊化;双方都必须应对不同的时间域。

 

世界不再是平的

• 具有私有数据的服务支持多台计算机协同工作。
• 服务及其服务边界意味着多个信任域和不同的事务域。

• 多个事务域意味着多个时间域。
• 多个时间域迫使您应对歧义,以允许共存、合作和协同工作。

 

外部数据:不变性

本节讨论外部数据的属性。首先,每个数据项都需要被唯一标识,并具有不可变的内容,这些内容不会随着其副本的移动而改变。接下来,在不同位置和不同时间对数据的解释中可能会导致异常;“稳定”数据的概念避免了这些异常。本节还讨论了模式以及它们描述的消息。这引出了一个外部数据如何引用另一个数据以及不变性的含义的机制。最后,当外部数据由一组独立的、各自生活在自己的时间域中的服务创建时,它看起来是什么样的?

 

不可变和/或版本化数据

数据可以是不可变的。一旦写入不可变数据并为其指定标识符,其内容对于该标识符将保持不变。一旦写入,就无法更改。在许多环境中,不可变数据可能会被删除,并且标识符随后将映射到“没有当前数据”的指示,但它永远不会返回原始内容以外的数据。无论何时何地引用,不可变数据都是相同的。版本化数据是不可变的。如果您指定了某个数据集合的特定版本,您将始终获得相同的内容。

在许多情况下,与版本无关的标识符用于引用数据集合。一个例子是纽约时报。每天都会生成新版本的报纸(实际上,由于地区版本,每天都会生成多个版本)。要将与版本无关的标识符绑定到基础数据,首先需要转换为与版本相关的标识符。例如,对最近纽约时报的请求将转换为对 2005 年 1 月 4 日加利福尼亚版纽约时报的请求。

这是一个与版本相关的标识符,它产生了当天报纸该地区版本的不可变内容。无论您何时何地请求,当天此版本的内容都永远不会改变。关于特定报纸内容的信息要么可用,要么不可用。如果可用,答案始终相同。

 

不变性、消息和外部数据

消息传递的一个现实是,消息有时会丢失。为了确保传递,必须重试消息。至关重要的是,重试必须具有相同的内容。消息本身必须是不可变的。一旦发送消息,就无法撤回消息,就像政治家无法撤回在电视上说的话一样。最好将每条消息都视为唯一标识的,并且该标识符必须为消息产生不可变的内容。这意味着对于给定的消息,始终返回相同的位。

 

数据的稳定性

不变性不足以确保不会造成混淆。对数据内容的解释必须明确。稳定数据在空间和时间上具有明确且不变的解释。

例如,每月银行对账单是稳定数据。其解释在空间和时间上是不变的。另一方面,布什总统这个词在 2005 年的含义与 1990 年的含义不同。在没有其他限定数据的情况下,这些词是不稳定的。同样,任何称为当前(例如,当前库存)的事物都是不稳定的。

为了确保数据的稳定性,重要的是设计在空间和时间上都明确的值。创建稳定数据的一种极好的技术是使用时间戳和/或版本控制。另一个重要的技术是确保重要的标识符(例如客户 ID)永远不会被重用。

不可变的模式和不可变的消息

如前所述,当发送消息时,它必须是不可变的和稳定的,以确保其正确解释。此外,消息的模式也必须是不可变的。因此,建议对所有消息模式进行版本控制,并且每条消息都使用消息格式精确定义的与版本相关的标识符。或者,模式可以嵌入到消息中。这在使用 JSON 或其他半结构化格式时很流行。

 

对数据的引用、不变性和 DAG

有时,引用其他数据至关重要。当从外部引用数据时,用于引用的标识符必须指定不可变的数据。

如果您找到一份不可变文档,告诉您阅读今天的纽约时报以了解更多详细信息,那么如果没有更多详细信息(特别是报纸的日期和地区),这对您没有任何好处。

随着新数据的生成,它可能引用其他数据项的复杂图,每个数据项都是不可变的并且是唯一标识的。这创建了一个引用的数据项的 DAG(有向无环图)。请注意,此模型允许每个数据项仅使用 DAG 中的另一个弧来引用其模式。

随着时间的推移,独立的服务(每个服务都在自己的时间域内)将生成新的数据项,而这些数据项完全忽略了其他服务的最新贡献。创建通过属于此 DAG 而相互关联的新不可变数据项是外部数据具有特殊魅力之处。

 

外部数据:参考数据

参考数据是指由单个服务创建和/或管理并发布给其他服务以供其使用的一种信息。每条参考数据都具有与版本无关的标识符和多个版本,每个版本都标有与版本相关的标识符。对于每条数据,都只有一个发布服务。

本节讨论版本的发布,然后转向参考数据的各种用途。

 

发布版本化参考数据

这里的想法非常简单。为某些数据创建与版本无关的标识符。一个服务是该数据的所有者,并定期发布一个新版本,该版本标有与版本相关的标识符。重要的是,已知版本的标识符随着后续版本的传输而增加。

当传输参考数据的版本时,必须假定它在某种程度上已过时。该信息显然来自过去,而不是现在。将这些版本视为快照是合理的。

 

参考数据的用途

到目前为止,参考数据有三个广泛的用途类别

• 操作数包含服务发布的信息,以预期另一个服务将使用这些值提交运算符。

• 历史记录描述了在发送服务范围内过去发生的事情。

• 共享集合包含一组相关服务之间共同拥有的信息,这些信息会随着时间的推移而演变。一个服务是保管人,并管理对集合一部分的更改的应用。其他服务使用较旧版本的信息。

 

操作数

如前所述,消息包含映射到服务提供的功能的运算符。这些运算符通常需要操作数作为描述请求工作详细信息的附加数据。操作数是从参考数据中收集的,参考数据通常由被调用的服务发布。例如,百货商店目录是用于填写订单的参考数据。在线零售商的价格表、产品目录和运费表都是操作数。

历史记录

历史记录报告过去发生的事情。有时,需要将这些历史快照从一个服务发送到另一个服务。除非在披露从一个服务到另一个服务的历史记录时采取适当的谨慎措施,否则可能会导致严重的隐私问题。因此,很多时候,这种使用模式在具有某种形式信任关系的服务之间可见,例如季度销售业绩、每月银行对账单、季度末的库存状态。

共享集合

参考数据最具挑战性的使用模式是共享集合。在这种情况下,许多不同的服务需要查看一些有趣数据的最新视图。经常引用的示例包括员工数据库和客户数据库。在每个数据库中,许多单独的服务都希望检查和更改这些集合中的数据。

许多大型企业都经历了大规模的这个问题。许多不同的应用程序认为他们可以更改客户数据库,并且现在这些应用程序在许多服务器上运行,因此客户数据库有许多副本(通常具有不兼容的模式)。对一个副本所做的更改会逐渐渗透到其他副本,并因模式转换和冲突更改而导致信息丢失。共享集合提供了一种机制,可以使拥有多个更新程序的愿望合理化,并允许控制业务逻辑来执行数据策略。共享集合有一个特殊的服务,该服务实际上拥有集合的权威视角。它执行业务规则,以确保数据的完整性。所有者服务定期发布集合的版本,并支持操作员请求更改的传入请求。

请注意,这不是乐观并发控制。所有者服务完全控制要对数据进行的更改。某些字段可能是可更新的,而另一些字段可能不是。在考虑每个请求的更改时,可能会应用业务约束。

考虑更改客户的地址。这不仅仅是一个简单的更新,而是复杂的业务逻辑

• 您不仅仅是简单地更新地址。您会附加新地址,同时记住旧地址在一段时间内有效。

• 更改地址可能会影响税务地点。

• 更改地址可能会影响销售区域。

• 可能需要重新安排发货。

内部数据

如前所述,内部数据封装在服务的应用程序逻辑之后。这意味着修改数据的唯一方法是通过服务的应用程序逻辑。有时,服务会导出其内部数据的一个子集,以在外部用作参考数据。

本节考察了内部数据的以下方面:(1)SQL 的模式定义语言运行的时间环境;(2)当外部数据到达服务时如何处理;以及(3)在外部数据中看到的可扩展性以及以碎片化的方式将该数据的副本存储在内部以方便其以关系形式使用的固有挑战。

 

SQL、DDL 和可串行化

SQL 的 DDL(数据定义语言)是事务性的。与 SQL 中的其他操作一样,通过 DDL 对模式的更新发生在事务的保护下,并以原子方式应用。这些模式更改可能会对数据库中存储的数据的解释方式产生重大影响。

至关重要的是,DDL 操作之前的事务应基于现有模式,而 DDL 操作之后的事务应基于操作更改的模式。换句话说,对模式的更改参与数据库的可串行化语义。

SQL 和 DDL 都存在于现在。每个事务仅在先前事务定义的模式上下文中才有意义。这种现在的概念是服务的时间域,该服务由服务逻辑及其数据库中包含的数据组成。

 

存储传入数据

当数据从外部到达时,大多数服务会将其复制到其本地 SQL 数据库中。尽管内部数据通常不是不可变的,但大多数服务选择实施一种约定,通过该约定,它们会不可变地保留数据。将传入数据在语法上转换为更方便服务使用的形式并不少见。这称为碎片化(图 4)。

Data on the Outside vs. Data on the Inside

 

很多时候,传入消息会作为精确的二进制副本保存,以用于审计和不可否认性,同时内容会转换为更易于在服务中使用的形式。

 

可扩展性与碎片化

通常,外部数据以半结构化表示形式(例如 JSON)保存,JSON 在这方面具有许多出色的特性,包括可扩展性。JSON 的可扩展性允许其他服务向消息中添加未在消息模式中声明的信息。基本上,消息的发送者添加了您在定义模式时未预料到的内容。在许多方面,可扩展性就像在纸质表格的边缘乱涂乱画。它经常获得期望的结果,但没有任何保证。

当传入的外部数据复制到 SQL 数据库中时,对其进行碎片化是有优势的。碎片化是将分层半结构化数据转换为关系表示形式的过程。规范化传入的外部数据不是优先事项。规范化旨在消除或减少更新异常。即使您将数据填充到 SQL 数据库中,您也不会更新它。您正在以一种更易于在 SQL 内部使用的方式捕获外部数据。然而,碎片化对于业务分析非常重要。关系映射越好,您就越能分析数据。

有趣的是,可扩展性会与碎片化作斗争。将计划外的扩展映射到计划内的表是很困难的。很多时候,会执行部分碎片化,其中符合众所周知且规则的模式表示形式的传入信息会被干净地碎片化为关系表示形式,而剩余数据(包括扩展)则在不进行碎片化的情况下保留。

 

数据的表示形式

让我们考虑一下 JSON 和 SQL 这两种突出数据表示形式的特性。

 

以 JSON 表示数据

JSON 是表示半结构化数据的标准。它是一种交换格式,具有人类可读的文本,用于存储和传输属性-值对。有时,数据的模式保存在 JSON 文档之外。有时,元数据(作为属性-值对)嵌入到文档的分层结构中。JSON 文档通常使用 URL 标识,这为文档提供了唯一的身份并允许对其进行引用。

正是这种人类可读性、自描述属性-值对以及通过 URL 实现的全局身份的结合使 JSON 如此受欢迎。当然,其在多种语言中出色的且易于使用的库也很有帮助。

 

以 SQL 表示数据

SQL 通过行和表内单元格中包含的值来表示关系。基于值的特性使其能够通过其值“关联”不同的记录。这正是 SQL关系骨干的本质。正是这种基于值的表示本质使得过去几十年中涌现出的惊人的查询技术成为可能。SQL 显然是内部数据表示形式的领导者。

 

有界和无界

让我们将 SQL 基于值的机制与 JSON 基于身份和引用的机制进行对比。

关系表示形式必须是有界的。为了使基于值的比较正确工作,必须同时存在时间和空间界限。只有当两个记录的内容都在同一模式中定义时,基于值的比较才有意义。只有当多个模式可以在同一时间范围内(即,在同一数据库中使用 ACID 语义)更新时,它们才能具有明确定义的含义。这有效地产生了一个单一模式。SQL 在语义上是基于集中管理的单一模式的。

过去 20 年来创建分布式 SQL 数据库的尝试是不错的,但必须包括单一事务范围和单一 DDL 模式。否则,关系代数的语义将面临压力。SQL 仅在单个数据库内部有效。

JSON 是无界的。在 JSON 中,数据是使用 URI(统一资源标识符)而不是值来引用的。这些 URI 是普遍定义的且唯一的。当然,每个 URL 都是合法的 URI,所以它们也很酷。URI 可以在任何计算机上使用,以唯一标识引用的数据。当与适当的纪律一起使用时,这可能会导致创建 JSON 文档的 DAG,每个文档都可能由生活在独立时间(和模式)域中的独立服务创建。

 

内部和外部数据的特性

让我们考虑一下迄今为止讨论的内部和外部数据的各种特性,如图 5 所示。

Data on the Outside vs. Data on the Inside

 

不变性、基于身份的引用、开放模式和 JSON 表示适用于外部数据,而不适用于内部数据。这都是数据表示形式的软件包的一部分,它很好地满足了外部数据的需求。不可变的数据项可以在整个网络中复制,并且可以由任何服务生成新的数据项。实际上,开放和独立的模式机制允许独立定义消息的新格式,从而进一步支持独立服务的独立性。

接下来,考虑封装并意识到外部数据不受代码保护。没有正式的概念来确保对数据的访问受到代码体的中介。相反,有一个设计要点,即如果您可以访问消息的原始内容,您应该能够理解它。内部数据始终由服务及其应用程序逻辑封装。

考虑数据及其与模式的关系。外部数据是不可变的,并且每个数据项的模式保持不变。请注意,模式可能是版本化的,新版本可以应用于后续的类似数据项,但这并不能改变一旦创建了特定的不可变项,其模式就保持不变的事实。这与 SQL 用于内部数据的机制形成鲜明对比。SQL 的 DDL 旨在允许在数据库填充时对现有模式进行强大的转换。

最后,让我们考虑对数据执行商业智能分析的愿望。经验表明,那些分析人员希望剖析和切块他们可以获得的任何东西。现有的分析主要在内部数据上运行,这将肯定会继续作为分析的素材。但是,毫无疑问,分析外部数据也很有用。

 

数据表示的动态二人组

现在,让我们比较这两种数据表示形式(SQL 和 JSON)的优点和缺点

• SQL 具有有界模式,非常适合比较任何事物与任何事物(但仅在界限内)。

• JSON 具有无界模式,支持模式和数据的独立定义。可扩展性也很酷。

 

Data on the Outside vs. Data on the Inside

 

 

考虑执行任意查询需要什么

SQL 非常出色,因为它具有基于值的性质和严格控制的模式,这确保了值的一致性,从而促进了查询的基础比较语义。

JSON 存在问题,因为模式不一致。正是定义的独立性带来了值对齐的挑战。此外,数据的分层形状和形式也可能令人头疼。尽管如此,您可以在易于查询的形式中投影一致的模式。这可能是有损投影,其中并非所有知识都可用于查询。

 

接下来,考虑共享数据的独立定义

SQL 是不可能的,因为它具有中心化模式。正如已经讨论过的,这是其在严格控制的环境中支持基于值的查询的能力所固有的。

JSON 非常出色。它专注于模式的独立定义和包含数据的文档的独立生成。这是 JSON 和其他半结构化数据表示形式的巨大优势。

 

每种模型的优势同时也是其劣势。

使 SQL 在查询方面出色的特性使其在独立定义共享数据方面变得糟糕。JSON 在独立定义方面非常出色,但在查询方面却很糟糕。您不能在不损害其优势的情况下向这些模型中的任何一个添加功能来解决其弱点。

 

结论

本文描述了服务和信任对数据处理的影响。它介绍了内部数据与外部数据的概念。在讨论了不跨服务边界共享事务的时间影响之后,本文考虑了外部数据中不变性和稳定性的需求。这导致将外部数据描绘为由不同服务独立生成的数据项的 DAG。

然后,本文研究了参考数据的概念及其在促进服务互操作中的使用模式。它简要概述了内部数据,并讨论了在面对可扩展性时切分传入数据的挑战。

最后,JSON 和 SQL 被视为数据的表示形式,并对其优点和缺点进行了比较和对比。这得出的结论是,这些模型中的每一种都在一种用法中具有优势,而这种优势弥补了其在另一种用法中的劣势。如今,使用 JSON 表示外部数据,使用 SQL 存储内部数据已成为常见的做法。这两种表示形式的使用方式都发挥了各自的优势。

 

这是对 2005 年在 CIDR(创新数据系统研究会议)上发表的同名原始论文的更新。当时,XML 比 JSON 更常用。同样,SOA(面向服务的架构)当时使用得更多,而今天,更常见的是简单地说“服务”。在本文中,“服务”用于表示由其服务或应用程序代码封装的数据库。它并不意味着微服务。那是另一篇论文的主题。抛开术语不谈,变化不大。

 

参考文献

1. Bernstein, P. A., Hadzilacos, V., Goodman, N. 1987. 数据库系统中的并发控制和恢复。 Addison-Wesley (http://sigmod.org/publications/dblp/db/books/dbtext/bernstein87.html)。

2. Gray, J., Reuter, A. 1993. 事务处理:概念和技术。Morgan Kaufmann。

 

相关文章

超越关系数据库
数据访问不仅仅是 SQL。
Margo Seltzer
https://queue.org.cn/detail.cfm?id=1059807

SQL 的独特成功
SQL 作为数据表示形式万神殿中的重要人物,拥有辉煌的未来。
Pat Helland
https://queue.org.cn/detail.cfm?id=2983199

大型共享数据库的协同关系数据模型
Erik Meijer 和 Gavin Bierman
与普遍的看法相反,SQL 和 noSQL 实际上只是同一枚硬币的两面。
https://queue.org.cn/detail.cfm?id=1961297

 

Pat Helland 自 1978 年以来一直从事事务系统、数据库、应用程序平台、分布式系统、容错系统和消息传递系统的实施工作。为了消遣,他偶尔会撰写技术论文。他目前在 Salesforce 工作。

版权所有 © 2020,所有者/作者持有。出版权已许可给 。

acmqueue

最初发表于 Queue vol. 18, no. 3
数字图书馆 中评论本文





更多相关文章

Ethan Miller, Achilles Benetopoulos, George Neville-Neil, Pankaj Mehra, Daniel Bittman - 远端内存中的指针
有效利用新兴的远端内存技术需要考虑在父进程上下文之外对丰富连接的数据进行操作。正在开发中的操作系统技术通过公开诸如内存对象和全局不变指针之类的抽象来提供帮助,设备和新实例化的计算可以遍历这些抽象。这些想法将使在具有分离内存节点的未来异构分布式系统上运行的应用程序能够利用近内存处理来获得更高的性能,并独立扩展其内存和计算资源以降低成本。


Simson Garfinkel, Jon Stewart - 磨砺你的工具
本文介绍了我们在最初发布十年后更新高性能数字取证工具 BE (bulk_extractor) 的经验。在 2018 年至 2022 年期间,我们将该程序从 C++98 更新为 C++17。我们还进行了完整的代码重构并采用了单元测试框架。DF 工具必须经常更新,以跟上其使用方式的变化。对 bulk_extractor 工具更新的描述可以作为可以而且应该做什么的一个例子。


Pat Helland - 自主计算
自主计算是一种使用协作来连接领地及其使节的业务工作模式。这种模式基于纸质表格,已经使用了几个世纪。在这里,我们解释了领地、协作和使节。我们研究了使节如何在自主边界之外工作,并且在保持局外人的同时很方便。我们还研究了如何启动跨不同领地的工作,长期运行并最终完成。


Archie L. Cobbs - 持久性编程
几年前,我的团队正在为一个增强型 911 (E911) 紧急呼叫中心商业 Java 开发项目工作。我们试图使用传统的 Java over SQL 数据库模型来满足该项目的数据存储需求,但感到沮丧。在对项目的特定需求(和非需求)进行一些反思之后,我们深吸一口气,决定从头开始创建我们自己的自定义持久层。





© 保留所有权利。

© . All rights reserved.