1. 关系模型和文档模型
关系模型:在SQL中称为表,每个关系都是元组(在SQL中为行)的无序集合
文档模型:通常用JSON/XML等格式的文档表述一个数据结构
1.1. 关系模型:对象-关系不匹配
关系模型下:对象存储到关系中需要进行匹配,因此需要一个转换层,即ORM,但依旧不能隐藏对象和关系的差别。
文档模型下:几乎不需要考虑这个问题,存储到一个文档即可(应用层只需要一个格式解析工具)。此外还可以提供良好的局部性。
1.2. 多对一、多对多关系
关系模型下:可以很方便地通过范式化,来表达多对一和多对多的关系,从而消除冗余。且可通过联结(join)操作,方便数据的修改,保持数据的一致。
文档模型下:多对一和多对多的关系通过树形结构进行保存,但是通常要保存引用以表示关联关系(或者直接保存数据,这样就消除了联结)。因此,它的联结能力较弱,常需要在应用层模拟联结操作。
不过在联结上,关系模型和文档模型实质上并没有什么不同,都保存为唯一标识符(关系模型为外键,文档模型为文档引用)。
1.3. 总结:关系与文档数据模型的对比
a) 应用代码
关系模型:倾向于数据的分解是合适的。有可能使模型更为笨重,代码可能更加复杂。
文档模型:倾向于数据类似于文档是合适的。但对于嵌套和联结会有限制。
若高度联结下,图模型更合适。
b) 模式灵活性
关系模型:有很大的限制,通常对模式的修改(如ALTER TABLE
)速度很慢,非常不灵活
文档模型:模式限制小,灵活,即保证“读时模式”(数据结构是隐式的,只在读取时解释)
而关系模型常保证“写时模式”,数据库确保写入的数据都遵循这种模式
c) 局部性
关系模型:数据常分布到不同表,需要多次I/O操作,局部性较差
文档模型:数据存储为连续的串,若对整个文档访问,则有局部性性能优势(仅适用于访问大部分数据情况下)
而文档模型在更新时,常整个覆写。因此,文档应该尽量小,且避免写入时增加文档大小。
2. 数据查询语言
2.1. 命令式查询
即通过一般的编程语言进行查询。
2.2. 声明式查询
2.2.1. 数据库上的查询
即通过如SQL语言/关系代数,以及其它NoSQL上的查询语言(如Cypher)来进行查询。
声明式查询有下列特点:
- 只需指定数据模式、结果条件、结果转换即可,更加简洁和易用
- 隐藏底层引擎的实现细节
- 虽然功能上有限制,但是给底层更多自动优化空间
- 适合并行执行
2.2.2. Web上的查询
常见的例子有:
- CSS选择器
- XPath
相比于用JavaScript对DOM的命令式操作,声明式查询更加简单
2.3. Map-Reduce查询
其不是声明式查询语言,也不是命令式查询API,而是:
- 趋于两者之间,查询逻辑用代码片段表示,可被处理框架重复调用
- 基于
map
(collect
)和reduce
(fold
/inject
)函数
其通常用于在集群上分布执行,较为底层。(而SQL可通过一些Map-Reduce操作pipeline实现)
3. 图状数据模型
NoSQL中,与文档模型相反,图状模型适用于关联关系非常多的场景。
3.1. 属性图
以Neo4j为代表。数据结构包含:
- 顶点,包含:
- 唯一标识符
- 出边、入边集合
- 属性集合(K-V pair)
- 边,包含:
- 唯一标识符
- 边开始顶点标识符
- 边结束顶点标识符
- 关系类型标签
- 属性集合(K-V pair)
具体可见这里。
a) Cypher查询语言
一种图查询的声明式语言,语法类似于SQL。最早在Neo4j中使用,具体可见这里以及官方文档。
b) SQL上的图查询
关系数据库可以表示图数据,因此也可以用SQL查询,不过比较困难。
查询图数据,在SQL上要使用JOIN
操作,而JOIN
带来的遍历开销是很大的,且查询变长路径很麻烦(因为要确定JOIN
的次数)。
而SQL 1999后,通过递归公用表表达式(WITH RECURSIVE
)可用于查询变长路径,但查询所需写的代码量远远大于类似Cypher的图查询语言。
3.2. 三元存储与SPARQL
三元存储模式即通过三元组存储图。
存储分为:主体、谓语、客体
-
主体:图中的顶点
-
客体:
-
原始数据类型的值,即
<sub, key, val>
表示主体sub
有一个属性<key, val>
,val
为客体此时,谓语为属性的键
-
图中的另一个顶点
此时,谓语为连接两点的边
-
SPARQL
一种采用RDF数据模型的三元存储查询语言,与Cypher也非常类似。具体可见这里。