第二章:数据模型与查询语言

数据模型可能是软件开发中最重要的部分,它不仅仅影响着软件的编写方式,而且影响着我们的解题思路

一个复杂的应用程序可能会有更多的中间层次,每个层都通过提供一个明确的数据模型来隐藏更低层次中的复杂性。

关系模型与文档模型

最著名的数据模型可能是SQL。它基于Edgar Codd在1970年提出的关系模型:数据被组织成关系(SQL中称作),其中每个关系是元组(SQL中称作)的无序集合

关系数据库起源于商业数据处理,在20世纪60年代和70年代用大型计算机来执行。典型的事务处理(将销售或银行交易,航空公司预订,库存管理信息记录在库)和批处理(客户发票,工资单,报告)。

NoSQL的诞生

采用NoSQL数据库的背后有几个驱动因素,其中包括:

  • 需要比关系数据库更好的可扩展性,包括非常大的数据集或非常高的写入吞吐量
  • 相比商业数据库产品,免费和开源软件更受偏爱。
  • 关系模型不能很好地支持一些特殊的查询操作
  • 受挫于关系模型的限制性,渴望一种更具多动态性与表现力的数据模型

不同的应用程序有不同的需求,一个用例的最佳技术选择可能不同于另一个用例的最佳技术选择。关系数据库会与各种非关系数据库一起使用 - 混合持久化(polyglot persistence)

对象关系不匹配

如果数据存储在关系表中,那么需要一个笨拙的转换层,处于应用程序代码中的对象和表,行,列的数据库模型之间。模型之间的不连贯有时被称为阻抗不匹配(impedance mismatch)。

对象关系映射(ORM object-relational mapping) 框架可以减少这个转换层所需的样板代码的数量,但是它们不能完全隐藏这两个模型之间的差异。

我们可以使用JSON模型减少了应用程序代码和存储层之间的阻抗不匹配,但是JSON作为数据编码格式也存在问题。

JSON表示比数据库的多表模式具有更好的局部性(locality)

多对一和多对多的关系

存储ID还是文本字符串,这是个 副本(duplication) 问题。当使用ID时,对人类有意义的信息(比如单词:Philanthropy)只存储在一处,所有引用它的地方使用ID(ID只在数据库中有意义)。当直接存储文本时,对人类有意义的信息会复制在每处使用记录中。

使用ID的好处是,ID对人类没有任何意义,因而永远不需要改变:ID可以保持不变,即使它标识的信息发生变化

任何对人类有意义的东西都可能需要在将来某个时候改变——如果这些信息被复制,所有的冗余副本都需要更新。这会导致写入开销,也存在不一致的风险(一些副本被更新了,还有些副本没有被更新)。去除此类重复是数据库 规范化(normalization) 的关键思想。

数据库通过连接来进行多表关联查询,来使得数据变得更加互联。

文档数据库是否在重蹈覆辙?

在多对多的关系和连接已常规用在关系数据库时,文档数据库和NoSQL重启了辩论:如何最好地在数据库中表示多对多关系

20世纪70年代最受欢迎的业务数据处理数据库是IBM的信息管理系统(IMS),设计中使用了一个相当简单的数据模型,称为层次模型:文档数据库使用的JSON模型有一些相似之处。它将所有数据表示为嵌套在记录中的记录树。

同文档数据库一样,IMS能良好处理一对多的关系,但是很难应对多对多的关系,并且不支持连接。

提出了两种解决方案来解决层次模型的局限性:

  • 关系模型(relational model)(它变成了SQL,统治了世界)
  • 网络模型(network model)

网络模型

网络模型被称为CODASYL模型。

在层次模型的树结构中,每条记录只有一个父节点;在网络模式中,每条记录可能有多个父节点

网络模型中记录之间的链接不是外键,而更像编程语言中的指针(同时仍然存储在磁盘上)。访问记录的唯一方法是跟随从根记录起沿这些链路所形成的路径。这被称为访问路径(access path)

访问路径类似遍历链表:从列表头开始,每次查看一条记录,直到找到所需的记录。但在多对多关系的情况中,数条不同的路径可以到达相同的记录。

关系模型

关系模型中一个 关系(表) 只是一个 元组(行) 的集合

你可以选中符合任意条件的行,读取表中的任何或所有行。你可以通过指定某些列作为匹配关键字来读取特定行。你可以在任何表中插入一个新的行,而不必担心与其他表的外键关系。

在关系数据库中,查询优化器自动决定查询的哪些部分以哪个顺序执行,以及使用哪些索引。

关系模型的一个关键洞察是:只需构建一次查询优化器,随后使用该数据库的所有应用程序都可以从中受益。如果你没有查询优化器的话,那么为特定查询手动编写访问路径比编写通用优化器更容易——不过从长期看通用解决方案更好。

与文档数据库相比

文档数据库还原为层次模型:在其父记录中存储嵌套记录,而不是在单独的表中。

在表示多对一和多对多的关系时,关系数据库和文档数据库并没有根本的不同:在这两种情况下,相关项目都被一个唯一的标识符引用,这个标识符在关系模型中被称为外键,在文档模型中称为文档引用。

关系型数据库与文档数据库在今日的对比

文档数据模型:架构灵活性,局部性而拥有更好的性能,更接近于应用程序使用的数据结构。

关系模型:更好的支持多对一和多对多的关系

文档模型中的架构灵活性

文档数据库有时称为无模式(schemaless),但这具有误导性。一个更精确的术语是读时模式(schema-on-read)数据的结构是隐含的,只有在数据被读取时才被解释),相应的是写时模式(schema-on-write)传统的关系数据库方法中,模式明确,且数据库确保所有的数据都符合其模式

读时模式类似于编程语言中的动态(运行时)类型检查,而写时模式类似于静态(编译时)类型检查。

在应用程序想要改变其数据格式的情况时:

  • 在文档数据库中,只需开始写入具有新字段的新文档,并在应用程序中使用代码来处理读取旧文档的情况。
  • 在“静态类型”数据库模式中,通常会执行以下 迁移(migration) 操作。

读时模式优势:

  • 存在许多不同类型的对象,将每种类型的对象放在自己的表中是不现实的。
  • 数据的结构由外部系统决定。你无法控制外部系统且它随时可能变化。

查询的数据局部性

文档通常以单个连续字符串形式进行存储,编码为JSON,XML或其二进制变体(如MongoDB的BSON)。

局部性仅仅适用于同时需要文档绝大部分内容的情况

更新文档时,通常需要整个重写。只有不改变文档大小的修改才可以容易地原地执行。因此,通常建议保持相对小的文档,并避免增加文档大小的写入

为了局部性而分组集合相关数据的想法并不局限于文档模型:

  • Oracle类似地允许使用一个称为 多表索引集群表(multi-table index cluster tables) 的类似特性。
  • Bigtable数据模型(用于Cassandra和HBase)中的 列族(column-family) 概念与管理局部性的目的类似。

文档和关系数据库的融合

大多数关系数据库系统(MySQL除外)都已支持XML。这包括对XML文档进行本地修改的功能,以及在XML文档中进行索引和查询的功能。

随着时间的推移,关系数据库和文档数据库似乎变得越来越相似,数据模型相互补充,如果一个数据库能够处理类似文档的数据,并能够对其执行关系查询,那么应用程序就可以使用最符合其需求的功能组合

数据查询语言

SQL是一种 声明式 查询语言,而IMS和CODASYL使用 命令式 代码来查询数据库。

命令式语言告诉计算机以特定顺序执行某些操作。

在声明式查询语言(如SQL或关系代数)中,你只需指定所需数据的模式 - 结果必须符合哪些条件,以及如何将数据转换(例如,排序,分组和集合) - 但不是如何实现这一目标。

声明式语言往往适合并行执行。

命令代码很难在多个内核和多个机器之间并行化,因为它指定了指令必须以特定顺序执行。

MapReduce查询

MapReduce是一个由Google推广的编程模型,用于在多台机器上批量处理大规模的数据。

MapReduce既不是一个声明式的查询语言,也不是一个完全命令式的查询API,而是处于两者之间:查询的逻辑用代码片断来表示,这些代码片段会被处理框架重复性调用。它基于(也称为)和(也称为)函数。

map和reduce函数在功能上有所限制:它们必须是函数,这意味着它们只使用传递给它们的数据作为输入,它们不能执行额外的数据库查询,也不能有任何副作用。

图数据模型

一个图由两种对象组成:顶点(vertices)(也称为节点(nodes)实体(entities)),和边(edges)( 也称为关系(relationships)弧 (arcs) )。多种数据可以被建模为一个图形

图中的所有顶点代表了相同类型的事物。图提供了一种一致的方式,用来在单个数据存储中存储完全不同类型的对象。

属性图

在属性图模型中:

每个顶点(vertex)包括:

  • 唯一的标识符
  • 一组 出边(outgoing edges)
  • 一组 入边(ingoing edges)
  • 一组属性(键值对)

每条 边(edge) 包括:

  • 唯一标识符
  • 边的起点/尾部顶点(tail vertex)
  • 边的终点/头部顶点(head vertex)
  • 描述两个顶点之间关系类型的标签
  • 一组属性(键值对)

可以将图存储看作由两个关系表组成:一个存储顶点,另一个存储边

关于这个模型的一些重要方面是:

  1. 任何顶点都可以有一条边连接到任何其他顶点。没有模式限制哪种事物可不可以关联。
  2. 给定任何顶点,可以高效地找到它的入边和出边,从而遍历图,即沿着一系列顶点的路径前后移动。
  3. 通过对不同类型的关系使用不同的标签,可以在一个图中存储几种不同的信息,同时仍然保持一个清晰的数据模型。

Cypher查询语言

Cypher是属性图的声明式查询语言,为Neo4j图形数据库而发明。

SQL中的图查询

查询可变长度遍历路径的思想可以使用称为递归公用表表达式。

三元组存储和SPARQL

在三元组存储中,所有信息都以非常简单的三部分表示形式存储(主语谓语宾语)。例如,三元组 (吉姆, 喜欢 ,香蕉) 中,吉姆 是主语,喜欢 是谓语(动词),香蕉 是对象。

三元组的主语相当于图中的一个顶点。而宾语是下面两者之一:

  1. 原始数据类型中的值,例如字符串或数字。在这种情况下,三元组的谓语和宾语相当于主语顶点上的属性的键和值。例如,(lucy, age, 33)就像属性{“age”:33}的顶点lucy。
  2. 图中的另一个顶点。在这种情况下,谓语是图中的一条边,主语是其尾部顶点,而宾语是其头部顶点。例如,在(lucy, marriedTo, alain)中主语和宾语lucyalain都是顶点,并且谓语marriedTo是连接他们的边的标签。

posted on 2023-05-18 16:13  Mr.Tan&  阅读(57)  评论(0编辑  收藏  举报

导航