【DDIA笔记】第二章 、数据模型与查询语言

276 阅读8分钟

数据模型

数据模型可能是软件开发最重要的部分,它对软件的编写方式和如何思考待解决的问题都有深远影响。

大多数应用都是通过一层一层叠加数据模型来构建的。每一层的关键问题是:如何表示下一层?如:

  1. 通过对象或数据结构,以及操作这些数据的API来对现实世界进行建模
  2. 存储这些数据结构时,可以采用通用的数据模型(如JSON、XML、关系数据库中的表或图模型)来表示
  3. 数据库需要决定用何种内存、磁盘或网络的字节格式来表示上述JSON/XML/关系/图数据。
  4. 在更下一层,需要考虑用电流、光脉冲、磁场等来表示字节

关系模型和文档模型

关系模型:数据被组织成关系(releations),在SQL中称为表(table),其中每个关系都是元组(tuples)的无序集合(在SQL称为行)。

采用NoSQL的驱动因素

  • 扩展性好、支持大数据集或超高写入吞吐量
  • 免费、开源
  • 关系模型不支持一些特定的查询操作
  • 渴望更具动态和表达力的数据模型

对象—关系不匹配问题

如果数据存储在关系表中,那么应用层代码中的对象与表、行和列的数据模型之间需要一个转换层。模型之间的脱离有时被称为阻抗不匹配。

虽然ORM框架可以减少转换层所需的样板代码量,但是他们并不能完全隐藏两个模型之间的差异。

多对一与多对多关系

对于多对一或多对多关系的存储,无论是存储ID还是实际的数据,都涉及内容重复问题。

数据库规范化需要表达多对一的关系,这并不是很适合文档模型。

对于关系型数据库,支持join操作,可以方便地通过ID来引用其他表中的行。而在文档数据库中,一对多的树状结构不需要join,对join的支持也很弱。

几种数据模型

层次模型

与文档数据库中的JSON类似,将所有数据表示为嵌套在记录中的记录(树)。

特点:

  • 很好地支持一对多关系,但对多对多关系的支持有点困难
  • 不支持join

为了解决层次模型的局限性,提出了关系模型和网络模型

网络模型

网络模型由数据系统语言会议(Conference on Data System Languages,CODASYL)的委员会进行标准化,因此也被称为CODASYL模型。

特点:

  • 一个记录可能有多个父节点
  • 记录之间不是通过外键,而是通过”指针”链接
  • 访问记录的唯一方法是选择一条始于根记录的路径,并沿着相关的链接一次访问。这条链接也因此被称为访问路径。

缺点:

  • 查询和更新数据库复杂、且不灵活

关系模型

定义了所有数据的格式:关系(表)只是元组(行)的集合,仅此而已。

特点:

  • 没有复杂的嵌套结构,也没有复杂的访问路径。
  • 支持任意条件查询
  • 优化器自动决定以何种顺序执行查询,无需程序员维护
  • 动态添加索引,无需更改查询即可利用索引

关系模型的一个核心要点是:只需构建一次查询优化器,然后使用该数据库的所有应用都可以从中受益。

文档模型

文档数据库是某种方式的层次模型:即在其父记录中保存了嵌套记录,而不是存储在单独的表中。

但在表示多对一或多对多关系时,关系数据库和文档数库并没有根本不同:在这两种情况下,相关项都由唯一的标识符引用,该标识符在关系模型中被称为外键,在文档中被称为文档引用。标识符可以在查询时通过join操作或相关后续查询来解析。

关系模型 vs 文档模型

文档模型:

  • 模式(schema)灵活

    • 大多数文档数据库,不会对文档中的数据强制执行任何模式

    • 读时模式(schema-on-read):数据结构是隐式的,只有在读取时才解释

      关系数据库是写时模式,数据写入的时候必须遵循

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

  • 更好地局部性

    • 文档通常存储为JSON、XML或其他二进制的连续字符串。如果应用程序需要频繁访问整个文档,则存储局部性具有性能优势。如果数据划分到多个表中进行存储,则需要进行多次索引查找来检索所有数据,中间可能需要更多的I/O并花费更多的时间
    • 仅适用于需要同时访问文档大部分内容的场景。如果只访问其中的一小部分,对于大文档来说就有些浪费。
  • 更接近于应用程序所使用的数据结构

关系模型:

  • join操作
  • 表达多对一、多对多关系更简洁

数据查询语言

声明式 vs 命令式

命令式:告诉计算机以特定的顺序执行某些操作

声明式:只需执行所需的数据模式,结果需满足什么条件,以及如何转换数据,而不需指明如何实现这一目标。

  • 简洁、易用
  • 隐藏了数据库引擎的实现细节,这样数据库系统能够在不改变查询语言的情况下提高性能
  • 适合于并行执行。命令式代码由于指定了特定的执行顺序,很难在多核和多台机器上并行化。声明式语言仅指定了结果所满足的模式,并不指定如何得到结果的具体算法。

MapReduce查询

MapReduce是一种编程模型,用于在多台机器上批量处理海量数据。

MapReduce既不是声明式查询语言,也不是一个完全命令式的查询API,而是介于两者之间:查询的逻辑用代码片段表示,这些代码片段可以被处理框架重复地调用。

它包含两个函数:

  • map函数(collect):从匹配到的文档中提取数据。每个文档调用一次map函数。
  • reduce函数(fold或inject):对map的结果进行处理。只调用一次。

图状数据模型

多对多关系是不同数据模型之间的重要区别特征。如果数据大多是一对多的关系(树结构数据)或者记录之间没有关系,那么文档模型是最合适的。

但是,如果多对多的关系在数据中很常见。关系模型能够处理简单的多对多关系,但是随着数据之间的关联越来越复杂,将数据模型转化为图模型会更加自然。

很多数据可以建模为图。例子:

  • 社交网络
  • Web图
  • 公路或地铁网

构建图的方法:

  • 属性图模型(property graph,以Neo4j,Titan和InfiniteGraph为代表)
  • 三元存储模型(triple-store,以Datomic,AllegroGraph为代表)

声明式图查询语言:

  • Cypher
  • SPARQL
  • Datalog

命令式图查询语言:

  • Gremlin
  • Pregel

属性图

在属性图模型中,每个顶点包括:

  • 唯一的标识符
  • 出边的集合
  • 入边的集合
  • 属性的集合(键-值对)

每个边包括:

  • 唯一的标识符
  • 边开始的顶点(尾部节点)
  • 边结束的顶点(头部节点)
  • 描述两个顶点间关系类型的标签
  • 属性的集合(键-值对)

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

图模型值得注意的地方:

  1. 任何顶点都可以连接到其他任何顶点
  2. 给定某个顶点,可以高效地得到它的所有入边和出边,从而遍历图,即沿着这些顶点链条一直向前或向后
  3. 通过对不同类型的关系使用不同的标签,可以在单个图中存储多种不同类型的信息,同时仍然包保持整洁的数据模型

Cypher查询语言

Cypher是一种用于属性图的声明式查询语言,最早为Neo4j图形数库而创建。

三元存储与SPARQL

三元存储模式几乎等同于属性图模型,只是使用不同的名词描述了相同的思想。在三元图中,所有信息都以非常简单的三部分形式存存储(主体,谓语,客体)。例如,在三元组(吉姆,喜欢,香蕉)中,吉姆是主体,喜欢是谓语(动词),香蕉是客体。

三元组的主题相当于图中的定义。而客体则是一下两种之一:

  1. 原始数据类型中的值,如字符串或数字。此时,三元组的谓语和客体分别相当于主体(顶点)属性中的键和值。
  2. 图中的另一顶点。此时,谓语是图中的边,主体是尾部顶点,而客体是头部顶点。

语义网

语义网的思想是将网站上的信息发布为机器可读的格式给计算机阅读。

资源描述框架(Resouce Description Framework,RDF)就是这样一个机制,它让不同网站一一致的格式发布数据,这样来自不同网站的数据自动合并成一个网络数据,一种互联网级别包含所有数据的数据库。