现代信息系统相当复杂。 最重要的是,它们的复杂性是由于它们处理的数据的复杂性造成的。 数据的复杂性通常在于所使用的数据模型的多样性。 因此,举例来说,当数据变得“大”时,有问题的特征之一不仅是其数量(“volume”),而且是其多样性(“variety”)。
如果您还没有发现推理中的缺陷,请继续阅读。
多语言持久性
上述导致了这样一个事实:有时即使在一个系统的框架内,也需要使用多个不同的DBMS来存储数据并解决处理数据的各种问题,每个DBMS都支持自己的数据模型。 凭借 M. Fowler 的轻手,
Fowler还有以下在电子商务领域的功能齐全、高负载的应用程序中组织数据存储的例子。
当然,这个例子有些夸张,但是可以找到一些有利于为相应目的选择一个或另一个 DBMS 的考虑因素,例如,
显然,在这样的动物园里当仆人并不容易。
- 执行数据存储的代码量与所使用的 DBMS 数量成比例增长; 如果不与该数字的平方成正比,则同步数据的代码量就很好。
- 随着所使用的 DBMS 数量的倍数增加,为所使用的每个 DBMS 提供企业特性(可扩展性、容错性、高可用性)的成本也会增加。
- 无法保证整个存储子系统的企业特性——尤其是事务性。
从动物园园长的角度来看,一切都是这样的:
- DBMS 制造商的许可和技术支持成本成倍增加。
- 人员过剩和期限延长。
- 由于数据不一致而造成的直接经济损失或处罚。
系统的总拥有成本 (TCO) 显着增加。 有没有办法摆脱“多种存储选择”的局面呢?
多型号
“多元存储”一词于 2011 年开始使用。 意识到该方法的问题并寻找解决方案花了几年的时间,到 2015 年,通过 Gartner 分析师的口,给出了答案:
- 来自“
NoSQL DBMS 市场指南 - 2015 »:
DBMS 及其架构和使用方式的未来是多模型的。
- 来自“
ODBMS 魔力象限 - 2016 »:
领先的运营 DBMS 将提供多种模型(关系型和非关系型)作为单一平台的一部分。
看来这次 Gartner 分析师的预测是正确的。 如果您转到页面
下表显示了 DBMS - 每个私人评级中的领先者,声称是多模型的。 对于每个 DBMS,都会指示原始支持的模型(曾经是唯一的模型)以及当前支持的模型。 还列出了将自己定位为“原始多模型”的 DBMS,并且根据创建者的说法,没有任何初始继承模型。
DBMS | 初始模型 | 其他型号 |
---|---|---|
神谕 | 关系型 | 图表、文档 |
MS SQL | 关系型 | 图表、文档 |
PostgreSQL的 | 关系型 | 图表*、文档 |
MarkLogic | 记录 | 图表、关系 |
MongoDB的 | 记录 | 键值、图表* |
DataStax | 宽柱 | 纪录片、图表 |
Redis的 | 核心价值 | 纪录片、图表* |
ArangoDB | - | 图表、文档 |
OrientDB | - | 图表、文档、关系 |
Azure CosmosDB | - | 图表、文档、关系 |
表格注释
表中的星号表示需要保留的语句:
- PostgreSQL DBMS不支持图数据模型,但该产品支持它
基于它 ,例如 AgensGraph。 - 就 MongoDB 而言,更正确的说法是谈论查询语言中图运算符的存在(
,$lookup
)而不是支持图模型,尽管当然,它们的引入需要在支持图模型的方向上在物理存储级别进行一些优化。$graphLookup
- 对于 Redis,我们指的是扩展
RedisGraph .
接下来,对于每个类,我们将展示如何在该类的 DBMS 中实现对多个模型的支持。 我们将认为关系模型、文档模型和图形模型是最重要的,并使用特定 DBMS 的示例来展示“缺失的模型”是如何实现的。
基于关系模型的多模型DBMS
目前领先的 DBMS 都是关系型的;如果 RDBMS 没有向多建模方向发展,那么 Gartner 的预测就不能被认为是正确的。 他们展示了。 现在,多模型 DBMS 就像一把瑞士刀,什么都做不好的想法可以直接指向 Larry Ellison。
然而,作者更喜欢在 Microsoft SQL Server 中实现多建模,在此示例中将描述 RDBMS 对文档和图形模型的支持。
MS SQL Server 中的文档模型
Habré 上已经有两篇关于 MS SQL Server 如何实现对文档模型的支持的优秀文章;我将仅限于简短的复述和评论:
MS SQL Server 支持文档模型的方式对于关系 DBMS 来说非常典型:建议将 JSON 文档存储在普通文本字段中。 对文档模型的支持是提供特殊的运算符来解析此 JSON:
提取标量属性值,JSON_VALUE
提取子文档。JSON_QUERY
两个运算符的第二个参数都是类似 JSONPath 的语法中的表达式。
抽象地说,我们可以说以这种方式存储的文档不是关系 DBMS 中的“一流实体”,与元组不同。 具体来说,在 MS SQL Server 中,目前 JSON 文档的字段没有索引,这使得很难使用这些字段的值来连接表,甚至使用这些值来选择文档。 但是,可以为此类字段创建计算列并在其上创建索引。
此外,MS SQL Server 还提供了使用运算符从表内容方便地构建 JSON 文档的能力 FOR JSON PATH
最后,MS SQL Server 允许您解决文档构建的相反问题:您可以使用以下命令将 JSON 分解为表: OPENJSON
CROSS APPLY
.
MS SQL Server 中的图形模型
Microsoft SQL Server 中也完全实现了对图 (LPG) 模型的支持 CREATE TABLE AS NODE
и CREATE TABLE AS EDGE
分别
第一类表与普通表类似,用于存储记录,唯一的外部区别是该表包含系统字段 $node_id
— 数据库中图节点的唯一标识符。
同样,第二类表也有系统字段 $from_id
и $to_id
,此类表中的条目清楚地定义了节点之间的连接。 使用单独的表来存储每种类型的关系。
让我们用一个例子来说明这一点。 让图形数据具有如图所示的布局。 然后,要在数据库中创建相应的结构,您需要运行以下 DDL 查询:
CREATE TABLE Person (
ID INTEGER NOT NULL,
name VARCHAR(100)
) AS NODE;
CREATE TABLE Cafe (
ID INTEGER NOT NULL,
name VARCHAR(100),
) AS NODE;
CREATE TABLE likes (
rating INTEGER
) AS EDGE;
CREATE TABLE friendOf
AS EDGE;
ALTER TABLE likes
ADD CONSTRAINT EC_LIKES CONNECTION (Person TO Cafe);
此类表的主要特点是,在针对它们的查询中,可以使用具有类似 Cypher 语法的图形模式(但是,“*
”等尚不支持)。 基于性能测量,还可以假设数据在这些表中存储的方式与数据在常规表中存储的方式不同,并且针对执行此类图形查询进行了优化。
SELECT Cafe.name
FROM Person, likes, Cafe
WHERE MATCH (Person-(friendOf)-(likes)->Cafe)
AND Person.name = 'John';
此外,在处理此类表时很难不使用这些图形模式,因为在普通 SQL 查询中解决类似问题时,需要付出额外的努力来获取系统“图形”节点标识符($node_id
, $from_id
, $to_id
; 出于同样的原因,此处未显示插入数据的查询,因为它们不必要地麻烦)。
为了总结 MS SQL Server 中文档和图形模型实现的描述,我要指出的是,主要从语言设计的角度来看,一个模型在另一个模型之上的这种实现似乎并不成功。 有必要用一种语言扩展另一种语言,这些语言并不完全“正交”,兼容性规则可能相当奇怪。
基于文档模型的多模型DBMS
在本节中,我想使用其中不是最流行的 MongoDB 示例来说明文档 DBMS 中多模型的实现(如前所述,它只有条件图运算符 $lookup
и $graphLookup
,不适用于分片集合),而是使用更成熟和“企业”DBMS 的示例
因此,让集合包含一组以下类型的 XML 文档(MarkLogic 还允许您存储 JSON 文档):
<Person INN="631803299804">
<name>John</name>
<surname>Smith</surname>
</Person>
MarkLogic 中的关系模型
可以使用以下命令创建文档集合的关系视图 value
在下面的示例中可以是任意 XPath):
<template >
<context>/Person</context>
<rows>
<row>
<view-name>Person</view-name>
<columns>
<column>
<name>SSN</name>
<value>@SSN</value>
<type>string</type>
</column>
<column>
<name>name</name>
<value>name</value>
</column>
<column>
<name>surname</name>
<value>surname</value>
</column>
</columns>
</row>
<rows>
</template>
您可以使用 SQL 查询(例如,通过 ODBC)对创建的视图进行寻址:
SELECT name, surname FROM Person WHERE name="John"
不幸的是,显示模板创建的关系视图是只读的。 当处理它的请求时,MarkLogic 将尝试使用
MarkLogic 中的图模型
有了对图(RDF)模型的支持,一切都差不多了。 再次在帮助下
<template >
<context>/Person</context>
<vars>
<var>
<name>PREFIX</name>
<val>"http://example.org/example#"</val>
</var>
</vars>
<triples>
<triple>
<subject><value>sem:iri( $PREFIX || @SSN )</value></subject>
<predicate><value>sem:iri( $PREFIX || surname )</value></predicate>
<object><value>xs:string( surname )</value></object>
</triple>
<triple>
<subject><value>sem:iri( $PREFIX || @SSN )</value></subject>
<predicate><value>sem:iri( $PREFIX || name )</value></predicate>
<object><value>xs:string( name )</value></object>
</triple>
</triples>
</template>
您可以使用 SPARQL 查询来处理生成的 RDF 图:
PREFIX : <http://example.org/example#>
SELECT ?name ?surname {
:631803299804 :name ?name ; :surname ?surname .
}
与关系模型不同,MarkLogic 通过另外两种方式支持图模型:
- DBMS 可以是 RDF 数据的成熟的独立存储(其中的三元组将被称为
管理 与上面描述的相反提取 ). - 特殊序列化中的 RDF 可以简单地插入到 XML 或 JSON 文档中(然后三元组将被称为
非托管 )。 这可能是机制的替代方案idref
等等
关于 MarkLogic 中事物“真正”如何工作的一个好主意是
“没有主模型”的多模型 DBMS
市场上也有一些DBMS最初将自己定位为多模型,没有任何继承的主模型。 这些包括
事实上,ArangoDB 和 OrientDB 都有“核心”模型。 在这两种情况下,这些都是它们自己的数据模型,是文档模型的概括。 这些概括主要是为了促进执行图和关系性质的查询的能力。
这些模型是唯一可在指定的 DBMS 中使用的模型;它们自己的查询语言旨在与它们一起使用。 当然,这样的模型和 DBMS 是有前途的,但是缺乏与标准模型和语言的兼容性使得无法在遗留系统中使用这些 DBMS——取代那里已经使用的 DBMS。
Habré 上已经有一篇关于 ArangoDB 和 OrientDB 的精彩文章:
ArangoDB
ArangoDB 声称支持图形数据模型。
ArangoDB 中图的节点是普通文档,边是特殊类型的文档,与常规系统字段一起,具有 (_key
, _id
, _rev
) 系统字段 _from
и _to
。 文档 DBMS 中的文档传统上被组合成集合。 表示边的文档集合在 ArangoDB 中称为边集合。 顺便说一句,边集合文档也是文档,因此 ArangoDB 中的边也可以充当节点。
初始数据
让我们来收藏一下 persons
,其文档如下所示:
[
{
"_id" : "people/alice" ,
"_key" : "alice" ,
"name" : "Алиса"
},
{
"_id" : "people/bob" ,
"_key" : "bob" ,
"name" : "Боб"
}
]
也来个收藏吧 cafes
:
[
{
"_id" : "cafes/jd" ,
"_key" : "jd" ,
"name" : "Джон Донн"
},
{
"_id" : "cafes/jj" ,
"_key" : "jj" ,
"name" : "Жан-Жак"
}
]
然后是集合 likes
可能看起来像这样:
[
{
"_id" : "likes/1" ,
"_key" : "1" ,
"_from" : "persons/alice" ,
"_to" : "cafes/jd",
"since" : 2010
},
{
"_id" : "likes/2" ,
"_key" : "2" ,
"_from" : "persons/alice" ,
"_to" : "cafes/jj",
"since" : 2011
} ,
{
"_id" : "likes/3" ,
"_key" : "3" ,
"_from" : "persons/bob" ,
"_to" : "cafes/jd",
"since" : 2012
}
]
查询和结果
ArangoDB 中使用的 AQL 语言的图形样式查询以人类可读的形式返回有关谁喜欢哪家咖啡馆的信息,如下所示:
FOR p IN persons
FOR c IN OUTBOUND p likes
RETURN { person : p.name , likes : c.name }
在关系风格中,我们“计算”关系而不是存储它们,这个查询可以像这样重写(顺便说一句,没有集合 likes
可以没有):
FOR p IN persons
FOR l IN likes
FILTER p._key == l._from
FOR c IN cafes
FILTER l._to == c._key
RETURN { person : p.name , likes : c.name }
两种情况的结果是相同的:
[
{ "person" : "Алиса" , likes : "Жан-Жак" } ,
{ "person" : "Алиса" , likes : "Джон Донн" } ,
{ "person" : "Боб" , likes : "Джон Донн" }
]
更多查询和结果
如果上面的结果格式似乎对于关系 DBMS 比对于文档 DBMS 更典型,您可以尝试此查询(或者您可以使用 COLLECT
FOR p IN persons
RETURN {
person : p.name,
likes : (
FOR c IN OUTBOUND p likes
RETURN c.name
)
}
结果将如下所示:
[
{ "person" : "Алиса" , likes : ["Жан-Жак" , "Джон Донн"] } ,
{ "person" : "Боб" , likes : ["Джон Донн"] }
]
OrientDB
在 OrientDB 中在文档模型之上实现图模型的基础是 LINK
, LINKLIST
, LINKSET
, LINKMAP
и LINKBAG
。 这些类型的值是链接或链接的集合
系统分配的文档标识符具有“物理意义”,表示记录在数据库中的位置,如下所示: @rid : #3:16
。 因此,引用属性的值实际上是指针(如在图模型中)而不是选择条件(如在关系模型中)。
与 ArangoDB 一样,OrientDB 中的边被表示为单独的文档(尽管如果边没有自己的属性,则可以将其表示为
初始数据
格式接近
[
{
"@type": "document",
"@rid": "#11:0",
"@class": "Person",
"name": "Алиса",
"out_likes": [
"#30:1",
"#30:2"
],
"@fieldTypes": "out_likes=LINKBAG"
},
{
"@type": "document",
"@rid": "#12:0",
"@class": "Person",
"name": "Боб",
"out_likes": [
"#30:3"
],
"@fieldTypes": "out_likes=LINKBAG"
},
{
"@type": "document",
"@rid": "#21:0",
"@class": "Cafe",
"name": "Жан-Жак",
"in_likes": [
"#30:2",
"#30:3"
],
"@fieldTypes": "in_likes=LINKBAG"
},
{
"@type": "document",
"@rid": "#22:0",
"@class": "Cafe",
"name": "Джон Донн",
"in_likes": [
"#30:1"
],
"@fieldTypes": "in_likes=LINKBAG"
},
{
"@type": "document",
"@rid": "#30:1",
"@class": "likes",
"in": "#22:0",
"out": "#11:0",
"since": 1262286000000,
"@fieldTypes": "in=LINK,out=LINK,since=date"
},
{
"@type": "document",
"@rid": "#30:2",
"@class": "likes",
"in": "#21:0",
"out": "#11:0",
"since": 1293822000000,
"@fieldTypes": "in=LINK,out=LINK,since=date"
},
{
"@type": "document",
"@rid": "#30:3",
"@class": "likes",
"in": "#21:0",
"out": "#12:0",
"since": 1325354400000,
"@fieldTypes": "in=LINK,out=LINK,since=date"
}
]
正如我们所看到的,顶点还存储有关传入和传出边的信息。 在
查询和结果
与 OrientDB 中 ArangoDB 示例中的查询类似的查询如下所示:
SELECT name AS person_name, OUT('likes').name AS cafe_name
FROM Person
UNWIND cafe_name
结果将以以下形式获得:
[
{ "person_name": "Алиса", "cafe_name": "Джон Донн" },
{ "person_name": "Алиса", "cafe_name": "Жан-Жак" },
{ "person_name": "Боб", "cafe_name": "Жан-Жак" }
]
如果结果格式再次看起来太“相关”,您需要删除该行 UNWIND()
[
{ "person_name": "Алиса", "cafe_name": [ "Джон Донн", "Жан-Жак" ] },
{ "person_name": "Боб", "cafe_name": [ "Жан-Жак" ' }
]
OrientDB 的查询语言可以描述为带有类似 Gremlin 插入的 SQL。 在2.2版本中,出现了类似Cypher的请求表单, MATCH
MATCH {CLASS: Person, AS: person}-likes->{CLASS: Cafe, AS: cafe}
RETURN person.name AS person_name, LIST(cafe.name) AS cafe_name
GROUP BY person_name
结果格式将与先前请求中的相同。 想想需要删除哪些内容才能使其更加“相关”,就像在第一个查询中一样。
Azure CosmosDB
在较小程度上,上面关于 ArangoDB 和 OrientDB 的内容也适用于 Azure CosmosDB。 CosmosDB 提供以下数据访问 API:SQL、MongoDB、Gremlin 和 Cassandra。
SQL API 和 MongoDB API 用于访问文档模型中的数据。 Gremlin API 和 Cassandra API - 分别用于访问图形和列格式的数据。 所有模型中的数据均以 CosmosDB 内部模型格式保存:
但用户选择的数据模型和使用的 API 在服务中创建帐户时是固定的。 无法以另一种模型的格式访问一个模型中加载的数据,如下所示:
因此,今天的Azure CosmosDB中的多模型只是使用一个制造商支持不同模型的多个数据库的能力,这并不能解决多变量存储的所有问题。
基于图模型的多模型 DBMS?
值得注意的是,市场上还没有基于图模型的多模型 DBMS(除了同时支持两个图模型的多模型:RDF 和 LPG;参见
在图模型形成的过程中就考虑了如何在图模型之上实现关系模型的问题。 如何
图方法中没有任何固有的东西可以阻止在图数据库上创建一个层(例如,通过适当的索引),该层支持关系视图,(1)从通常的键值对中恢复元组,(2)对按关系类型划分的元组。
在图模型之上实现文档模型时,您需要记住以下几点:
- JSON 数组的元素被认为是有序的,但从图的边的顶点发出的元素则不是;
- 文档模型中的数据通常是非规范化的;您仍然不希望存储同一嵌入文档的多个副本,并且子文档通常没有标识符;
- 另一方面,文档 DBMS 的思想是文档是现成的“聚合体”,不需要每次都重新构建。 要求图模型能够快速获取成品文档对应的子图。
一点广告
这篇文章的作者与NitrosBase DBMS的开发相关,其内部模型是图,外部模型——关系模型和文档模型——是其表示。 所有模型都是平等的:几乎任何数据都可以使用其自然的查询语言在其中任何一个中获得。 而且,在任何视图中,数据都可以更改。 更改将反映在内部模型中,并相应地反映在其他视图中。
我希望在下面的一篇文章中描述 NitrosBase 中的模型匹配是什么样子的。
结论
我希望读者已经或多或少地了解了所谓的多建模的总体轮廓。 多模型 DBMS 有很大不同,“多模型支持”可能看起来也不同。 要了解每种特定情况下什么是“多模型”,回答以下问题很有用:
- 我们是在谈论支持传统模型还是某种“混合”模型?
- 这些模型是否“平等”,或者其中一个模型是其他模型的主题?
- 模型之间是否“无关紧要”? 在一种模型中写入的数据可以在另一种模型中读取甚至覆盖吗?
我认为关于多模型 DBMS 的相关性的问题已经可以得到肯定的回答,但有趣的问题是在不久的将来,哪种类型的需求会更大。 看来支持传统模型(主要是关系模型)的多模型 DBMS 的需求将会更大; 多模型 DBMS 的普及提供了结合各种传统模型优点的新模型,这是一个更遥远的未来问题。
只有注册用户才能参与调查。
您使用多模型 DBMS 吗?
-
我们不使用它,我们将所有内容存储在一个 DBMS 和一个模型中
-
我们使用传统 DBMS 的多模型功能
-
我们实践多语言持久性
-
我们使用新的多模型 DBMS(Arango、Orient、CosmosDB)
19 位用户投票。 4 名用户弃权。
来源: habr.com