DDIA-2-数据模型与查询语言

语言的边界就是世界的边界

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

关系模型与文档模型

关系型 RDBMS

关系模型致力于将数据库本身的实现细节隐藏在更简洁的接口之后。数据被组织成表,每个关系都是Tuple的无序集合。

应用场景
你今天在网上看到的大部分内容依旧是由关系数据库来提供支持,无论是在线发布,讨论,社交网络,电子商务,游戏,软件即服务生产力应用程序等等内容。

需求Transaction

文档型 NoSQL

NoSQL数据库出现,有以下几个原因:

  1. 关系型数据库的扩展性不够,包括对非常大的数据集或者非常高的写入吞吐量。
  2. 开源免费。
  3. 关系模型不能很好的支持特殊性的查询操作。
  4. 渴望一种更具多动态性与表现力的数据模型

对象关系不匹配

如果数据存储在关系表中,那么需要一个笨拙的转换层,处于应用程序代码中的对象和表,行,列的数据库模型之间。模型之间的不连贯有时被称为阻抗不匹配(impedance mismatch)
像ActiveRecord和Hibernate这样的对象关系映射(object-relational mapping, ORM)框架可以减少这个转换层所需的样板代码的数量,但是它们不能完全隐藏这两个模型之间的差异

LinkedIn简历的例子,一对多

一个User和他相关的项目有一对多的关系。除了使用在其他表中加入UserID作为外键;使用有些SQL数据库支持的列的数据格式为JSON/XML,
它更适合用JSON来表示。因为,简单,它主要是一个自包含的文档。文档模型优势在于如果要读区一份简历,局部性更好。一次查询就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"user_id": 251,
"first_name": "Bill",
"last_name": "Gates",
"summary": "Co-chair of the Bill & Melinda Gates... Active blogger.",
"region_id": "us:91",
"industry_id": 131,
"photo_url": "/p/7/000/253/05b/308dd6e.jpg",
"positions": [
{
"job_title": "Co-chair",
"organization": "Bill & Melinda Gates Foundation"
},
{
"job_title": "Co-founder, Chairman",
"organization": "Microsoft"
}
],
"education": [
{
"school_name": "Harvard University",
"start": 1973,
"end": 1975
},
{
"school_name": "Lakeside School, Seattle",
"start": null,
"end": null
}
],
"contact_info": {
"blog": "http://thegatesnotes.com",
"twitter": "http://twitter.com/BillGates"
}
}

这样的结构意味着数据存在一对多的关系,也就是树状结构

多对一,多对多

建立例子中的region_id, industry_id,主要解决了重复问题。可以避免歧义,做本地化支持,更好的搜索,等等。消除重复,就是数据库范式化的核心思想。

IMS的层次模型

支持多对多的关系有些困难,而且不支持联结。

网络模型(network model) 已经淘汰

存指针,而不是外键。是一条开始于根(root)的路径。更改变得很困难。

关系模型(relational model)

关系模型使用外键访问,也类似一选择了一条访问路径,区别在于,这个是由查询优化器自动生产的,一般不必过多的考虑。

文档数据库的比较

在表示多对多关系时候,关系模型与文档模型并没有不同,都是有相关的一项唯一标示符引用。关系模型中叫外键,文档模型中叫文档引用。可以使用链接操作或者后续的操作来解析这个引用。

选哪个

要考虑多方面的差异,包括容错性,并发处理。

关于模型中的差异,以下,

哪个更简单

如果应用本身有着类似文档的结构(树状结构,一对多),那么使用NoSQL。

关系型模型,倾向于模型中数据的分解。他把文档结构分解成多个表,有可能使得模式更为笨重,增加代码复杂度。

文档模型的局限性有,不能直接读取文档中的嵌套项,而是要全部读取,例如读区用户id为251的用户中职位列表的第二个。

对于连接的支持,是否是问题取决于应用本身,需不需要这种场景。

对于高度关联的数据,文档模型可能不是很适合。

文档模式的灵活性

可以将任何key-value添加到文档数据库中,而且读取时,客户端也无法保证文档会包含那些字段。

文档模型是schema on read,
关系模型是schema on write。

读时模式类似于编程语言中的动态(运行时)类型检查,而写时模式类似于静态(编译时)类型检查。就像静态和动态类型检查的相对优点具有很大的争议性一样【22】,数据库中模式的强制性是一个具有争议的话题,一般来说没有正确或错误的答案。

允许某些原因下,集合中数据是异构的。

查询数据的局部性

局部性优势在于,需要同时访问文档中大部分内容时,加载一次文档即可。但如果每次只访问文档中的一小部分,有些浪费。

更改文档时,会重写整个文档,因此通常建议,文档应该尽量的小,并且避免写入时候增加文档大小。

NoSQL在为文档分配空间时候,会多分配一些,以防修改更新时候,空间不足,引起空间搬家的昂贵操作。

NoSQL与关系型数据库的结合

数据查询语言

SQL是一种声明式查询语言,与IMS和CODASYL命令式不同。

声明式查询语言不需要关心实现。由数据库查询引擎来优化实现。可以在不改变查询语句的情况下提高性能。可以利用并行执行的优化。

Web中的声明式查询就是CSS,比Javascript的命令式查询要好。

MapReduce查询 介于声明式和命令式之间

一些NoSQL数据存储(包括MongoDB和CouchDB)支持有限形式的MapReduce,作为在多个文档中执行只读查询的机制。

查询的逻辑用代码片断来表示,这些代码片段会被处理框架重复性调用。它基于map(也称为collect)和reduce(也称为fold或inject)函数,两个函数存在于许多函数式编程语言中。

map和reduce函数在功能上有所限制:它们必须是纯函数,这意味着它们只使用传递给它们的数据作为输入,它们不能执行额外的数据库查询,也不能有任何副作用。这些限制允许数据库以任何顺序运行任何功能,并在失败时重新运行它们。然而,map和reduce函数仍然是强大的:它们可以解析字符串,调用库函数,执行计算等等。

MapReduce是一个相当底层的编程模型,用于计算机集群上的分布式执行。像SQL这样的更高级的查询语言可以用一系列的MapReduce操作来实现(见第10章),但是也有很多不使用MapReduce的分布式SQL实现。请注意,SQL中没有任何内容限制它在单个机器上运行,而MapReduce在分布式查询执行上没有垄断权。

图状数据模型

解决复杂的多对多关系。

社交图谱

顶点是人,边指示哪些人彼此认识。

网络图谱

顶点是网页,边缘表示指向其他页面的HTML链接。

公路或铁路网络

顶点是交叉路口,边线代表它们之间的道路或铁路线。

图的存储,就是存定点和边。

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

这些特性让建模很灵活。有利于演化:添加应用功能时,容易扩展用,更能应对变化,使用合适的的数据结构。

Cypher查询语言

Neo4j图形数据库

SQL的图查询

与Cypher比显得笨拙

三元存储与SPARQL

所有信息都是三个部分存储(主体,谓语,客体)。

RDF数据模型

类似XML,更冗长。 不区分属性和边。

有工具支持生成RDF格式的数据模型。

SPARQL查询语言

采用RDF数据模型的三元存储查询语言。类似一次性SELECT操作完成。

Datalog基础

比较老,适合处理复杂数据。每次实现一块。