使用多模型数据库建模数据

大多数开发人员已经断断续续的关系建模,并且在以非关系方式模拟数据时,必须做出一个非平凡的精神飞跃。这本身就很难,但是当您使用的数据存储实际上具有多模型功能时会发生什么?作为一个行业,我们看到越来越多的数据库采用这种方式,并提供多种模型来存储和查询数据,作为其核心性质的一部分。例如,ArangoDB,CosmosDB,Couchbase,当然还有RavenDB。

例如,RavenDB为您提供了以下模型:

  • 文档(JSON) - 具有接受读写的任何节点的多主机。
    • 多个文件的ACID交易。
    • 简单/全文查询。
    • 映射/减少和聚合查询。
  • 二进制数据 - 文档附件。
  • 计数器(Map <string,int64>) - CRDT多主分布式计数器。
  • 键/值 - 通过Raft协议实现强大的分布式一致性。
  • 图形查询 - 在文档模型之上。
  • 修订 - 文档的内置审计跟踪

有了这么多选项,当您需要为数据建模时,为工作选择合适的工具可能会令人困惑。在这篇文章中,我的目标是了解RavenDB提供的选项,并指导您做出最佳选择。

您将使用的默认和最常见的模型将是文档模型。它是最适合业务数据的一种,您通常会遵循域驱动设计方法来建模数据和实体。换句话说,我们讨论的是Aggregates,其中每个文档都是一个整体。实体之间的引用要么完全是聚合(和文档)的本地,要么只是聚合之间。换句话说,一个文档中的值不能指向另一个文档中的值。它只能指向另一个文档。

您的大部分业务逻辑都将集中在汇总级别上。即使单个事务修改多个文档,大多数业务逻辑也是在每个聚合上独立完成的。处理这种情况的一个好方法是使用域事件。这允许您组成域逻辑的独立部分,而无需将其全部绑定在一个大结中。

到目前为止,我们讨论过修改文档,但是您对数据执行的大部分操作都是查询并将其呈现给用户。在这些情况下,您需要做出有意识和明确的决定。无论您的显示器型号是基于您的文档还是其他来源。您可以使用RavenDB ETL将数据投影到不同的数据库,并在此过程中将其形状更改为适当的视图模型。RavenDB ETL允许您将数据库中的一部分数据复制到另一个位置,并能够在发送结果时修改结果。这可以是一个很好的工具,可以帮助您在不编写大量代码的情况下桥接域模型和视图模型。

这对于域和业务规则具有高度复杂性的应用程序非常有用。对于大多数应用程序,您可以在查询时简单地投影相关数据,但对于更复杂的系统,您可能希望在读取模型和域模型之间进行严格的物理分离。在这种情况下,Raven ETL可以极大地简化促进将数据从写入侧移动(和转换)到读取侧的任务的任务。

在建模方面,我们还需要考虑RavenDB的map / reduce索引。这些允许您定义将在后台运行的聚合,换句话说,在查询时,工作已经完成。反过来,这会导致快速的聚合查询,并且可能是系统设计中的另一个因素。通常使用map / reduce索引将原始数据聚合为更可用的形式并使用结果。直接从索引或使用输出集合功能将map / reduce索引的结果转换为真实文档(可以是其他索引,聚合等)。

到目前为止,我们只涉及文档建模,介意。还有很多其他选择。我们将从最简单的选项 - 附件开始。从本质上讲,附件就是这样。将一些二进制数据附加到文档的方法。听起来很简单,从建模的角度来看,它有一些深刻的含义。显然,在某处存储二进制数据的需求并不新鲜,而且有很多方法可以解决它。在关系数据库中,varbinary(max)使用了列。在文档数据库中,我已经看到直接存储在文档中的原始二进制数据(作为原始二进制数据或BASE64编码值)。在大多数情况下,这不是一个好主意。它会破坏文档(和表格)的大小,并使数据管理变得复杂。将数据存储在文件系统上会导致一组不同的问题,协调数据库和文件系统之间的事务,组织数据,保护诸如“../../etc/passwd”,备份和恢复等路径,以及还有很多。

附件

这些都是您希望数据库为您处理的所有内容。同时,二进制数据但不是一部分有关该文件。出于这些原因,我们在RavenDB中使用附件模型。这意味着就像电子邮件中的附件一样。二进制数据不存储在文档中,但与之密切相关。附件的常见用例包括用户文档的个人资料图片,租赁文档上的签名,包含有关付款计划文档贷款详情的Excel电子表格,或来自房屋检查报告的相关图片。文档可以包含任意数量的附件,附件可以是任何大小。这使您可以自由地将附加数据(双关语)附加到文档中,而不会有任何麻烦。与文档类似,附件也可以在多主模式下工作,并将与文档一起在集群中复制。

计数器

虽然附件可以是任何原始二进制数据,并且只有结构的名称(和可选的mime类型),但计数器的定义要严格得多。柜台是......好吧,一个柜台。这很重要。在最基本的层面上,它只是一个与文档关联的命名64位整数。和附件一样,文档可能包含任意数量的此类计数器。但是为什么将64位整数附加到文档上很重要?如此小的东西怎么可能足够重要我们需要一个全新的概念呢?毕竟,我们难道不能仅仅将相同的计数器存储在文档中的属性中吗?

要理解为什么RavenDB有计数器,我们需要了解它们不是什么。它们都涉及到文件,但不是文件。这意味着对计数器的更新不会整体修改文档。反过来,这意味着计数器上的操作可以非常便宜,无论您在文档中有多少计数器或者修改计数器的频率如何。将计数器与文档分开可以让我们做几件重要的事情,包括:

  • 便宜的更新
  • 分布式修改

在多主集群中,如果任何节点可以接受任何写入,则在两个不相交的服务器上对同一值进行两次更新时,需要注意冲突。对于文档,RavenDB根据预定义的策略检测并解析它。在柜台的情况下,没有这种需要。使用CRDT存储RavenDB中的计数器。这种格式允许我们处理对相同值的并发修改,而不会丢失数据或昂贵的锁。这使得计数器适合经常变化的值。计数器的良好示例是跟踪页面或广告上的视图,您可以在多个服务器上分发操作,并且仍然可以达到正确的最终计数。这显然适用于增量和减量。

鉴于计数器基本上只是map <string,int64>, 你可能会认为这里没有任何建模工作,对吧?但事实证明,即使使用这个简单的界面,也可以完成相当多的工作。例如,当跟踪页面上的视图或特定包的下载时,我不仅对下载总数感兴趣,而且对每天的下载感兴趣。在这种情况下,每当我们想要注意另一个下载时,我们将增加整个下载的计数器和该特定日的下载的另一个计数器。换句话说,计数器的名称可以包含有意义的信息。

核心价值

到目前为止,我们所讨论的所有数据都以多主方式存储和访问。换句话说,我们可以选择集群中的任何节点并对其进行写入,并且它将被接受。在多个节点上同时修改的数据将合并(计数器),存储(附件)或解析(文档)。当您关心系统的整体可用性时,这非常棒,我们始终接受写入并始终继续前进。但情况并非总是如此,因为在某些情况下您可能需要在操作中具有更高程度的一致性。例如,如果您要销售固定数量的商品,则需要确定 两个买家同时点击“购买”不会因为他们的请求使用不同的数据库服务器而导致问题。

为了处理这种情况,RavenDB提供了Cmp Xcng模型。这是数据库的群集范围键/值存储,它允许您以一致的方式存储命名值(整数,字符串或JSON对象)。此功能允许您确保高价值数据的一致行为。实际上,您可以将此功能与群集范围的事务相结合。

群集范围的事务允许您将文档上的操作与Cmp Xcng操作组合在一起以创建单个一致的事务。此模式使您可以执行条件操作,以根据全局一致的Cmp Xcng值修改文档。

Cmp Xcng值的一个很好的例子包括悲观锁和它们的所有者,以生成一个集群范围的锁,无论集群发生了什么,它都保证一致且安全。其他示例可以是在群集中的所有节点中存储系统的全局配置。

图查询

图形数据存储用于保存有关节点及其之间边缘的数据。它们是处理社交网络,数据挖掘和在大型数据集中查找模式等任务的绝佳工具。从版本4.2开始,RavenDB支持图形查询,但它以一种新颖的方式实现。RavenDB中的节点很自然地是一个文档,但与其他功能(如附件和计数器)不同,边缘没有单独的物理存在。相反,RavenDB能够使用文档结构本身来推断文档之间的边缘。这种动态特性意味着,当您需要在现有数据库之上应用图形查询时,您不必进行大量准备工作。您可以直接开始发布图形查询,RavenDB将在幕后工作,以确保找到所有数据,并且也很快。

在现有文档结构上执行图形查询的功能非常强大,但它并不能减少对数据建模的需求,以便最好地利用这一点。那么这意味着什么,将数据建模为可在文档形式和图形操作中使用?通常,当您需要以图形方式对数据建模时,您主要考虑节点之间的连接。

在RavenDB中查看图形建模的一种方法是明确边缘,但我觉得这很尴尬和限制。通常最好自然地表达域模型,并允许边缘在使用时从底层数据中弹出。RavenDB中的边是包含对另一个文档的引用的属性(或嵌套对象)。如果嵌套对象中的边缘,则对象的所有属性也是边缘上的属性,并且可以对其进行过滤。

为获得最佳结果,您希望将边属性建模为可以显式引用的单个嵌套对象。这在建模数据时已经是最佳实践,以获得更好的凝聚力,但图形查询使这成为一项要求。

与其他图形数据库不同,RavenDB不仅限于图形表示。RavenDB中的图形查询能够充分利用RavenDB查询的功能,这意味着您可以使用空间查询启动图形操作,然后继续进行图形模式匹配的其余部分。您应该在准备查询中完成大部分工作,而不是将大部分时间花在图形操作上。

图形操作的一个常见示例是欺诈检测,其中图形查询用于检测使用针对相同地址的许多不同信用卡进行的多个订单。我们可以在map / reduce索引上定义一个源查询,而不是尝试仅使用图形操作来进行匹配,该索引将聚合同一地址上的订单的所有结果。这将大大减少数据库回答查询所需的工作量。

修订

我想在这篇(已经很长的)帖子中讨论的最后一个主题是修订版的概念。RavenDB允许数据库管理员定义修订策略,在这种情况下,RavenDB将自动且透明地维护文档上所有更改的不可变日志。这意味着如果需要,您可以使用内置的审计跟踪。除了审计跟踪之外,修订也是RavenDB中几个关键功能的一个非常重要的功能。

例如,使用修订版,您可以使用订阅获取数据库中所做更改的(先前的,当前的)版本的元组。这允许您定义一些非常有趣的后端进程,这些进程可以全面了解文档随时间发生的所有更改。这在回归分析,应用业务规则以及查看数据如何随时间变化方面非常有趣。

摘要

我试图将这篇文章保持在较高水平,而不是陷入细节之中。我可能会在一般情况下发布一些关于建模的帖子,我将非常感谢您提出的任何反馈或您可以提出的任何问题。

猜你喜欢

转载自blog.csdn.net/Tybyqi/article/details/86480805
今日推荐