第二章 数据模型和查询语言
该笔记主要是记录自己学习内容,所以不照搬整个精读小册,有需要直接戳原文链接和视频链接。
原文档链接:第二章:数据模型和查询语言 (qtmuniao.com)
视频链接:DDIA 读书逐章分享——第二章(下):图数据模型_哔哩哔哩_bilibili
本节围绕两个主要概念来展开。
如何分析一个数据模型?:
- 基本考察点:数据基本元素,和元素之间的对应关系(一对多,多对多)
- 利用几种常用模型来比较:(最为流行的)关系模型,(树状的)文档模型,(极大自由度的)图模型。
- schema 模式:强 Schema(写时约束);弱 Schema(读时解析)
如何考量查询语言?:
- 如何与数据模型关联、匹配
- 声明式(declarative)和命令式(imperative)
数据模型:
每层模型核心问题:如何用下一层的接口来对本层进行建模?
- 作为应用开发者, 你将现实中的具体问题抽象为一组对象、数据结构(data structure) 以及作用于其上的 API。
- 作为数据库管理员(DBA) ,为了持久化上述数据结构,你需要将他们表达为通用的数据模型(data model) ,如文档数据库中的XML/JSON、关系数据库中的表、图数据库中的图。
- 作为数据库系统开发者,你需要将上述数据模型组织为内存中、硬盘中或者网络中的字节(Bytes) 流,并提供多种操作数据集合的方法。
- 作为硬件工程师,你需要将字节流表示为二极管的电位(内存)、磁场中的磁极(磁盘)、光纤中的光信号(网络)。 这点作为一个应用开发者来说深有感受。
在每一层,通过对外暴露简洁的数据模型,我们隔离和分解了现实世界的复杂度。
这也反过来说明了,好的数据模型需有两个特点:
- 简洁直观
- 具有组合性 我理解这一段:不同的职业是对不同数据的层所处理的以及所希望的是不一样的,就像网络协议一样,每一层都有他们的职责。
[关系模型与文档模型]
[关系模型]
关系型数据库,代表:Mysql... ,也是在工作中比较常用的。 这类关系模型的特点都是使用树的结构。 关系模型诞生后有很多挑战者,比如 网状模型,层模型。直到近十年, 数据爆炸式增长,处理需求需要精细化,催生了数据模型百花齐放,这句话其实就是大数据方向的数据处理,以及AI训练增多了。
NoSql (Not only SQL)代表:MongoDB,Redis,ElasticSearch,Cassandra。 其催动因素有:
- 处理更大数据集:更强伸缩性、更高吞吐量
- 开源免费的兴起:冲击了原来把握在厂商的标准
- 特化的查询操作:关系数据库难以支持的,比如图中的多跳分析
- 表达能力更强:关系模型约束太严,限制太多
维护关系模型,需要建多个结构,但是如果能支持非简单的数据类型,那么就需要文档模型了。 文档模型:使用 Json 和 XML 的天然嵌套。支持嵌套类型数据的,不止支持简单数据类型【int,string】
关系模型:使用 SQL 模型就得将职位、教育单拎一张表,然后在用户表中使用外键关联。
这在关系数据库表设计时需要考虑,即如何控制冗余(duplication) 。会有几种范式(normalization) 来消除冗余。
文档型数据库很擅长处理一对多的树形关系,却不擅长处理多对多的图形关系。如果其不支持 Join,则处理多对多关系的复杂度就从数据库侧移动到了应用侧。
[文档型 vs 关系型]
根据数据类型来选择数据模型
| 文档型 | 关系型 | |
|---|---|---|
| 对应关系 | 数据有天然的一对多、树形嵌套关系,如简历。 | 通过外键+ Join 可以处理 多对一,多对多关系 |
| 代码简化 | 数据具有文档结构,则文档模型天然合适,用关系模型会使得建模繁琐、访问复杂。 | |
| 但不宜嵌套太深,因为只能手动指定访问路径,或者范围遍历 | 主键,索引,条件过滤 | |
| Join 支持 | 对 Join 支持的不太好 | 支持的还可以,但 Join 的实现会有很多难点 |
| 模式灵活性 | 弱 schema,支持动态增加字段 | 强 schema,修改 schema 代价很大 |
| 访问局部性 | 1. 一次性访问整个文档,较优 2. 只访问文档一部分,较差 | 分散在多个表中 |
| 优势 | 1. 模式灵活:可以动态增删字段,如工作经历。2. 更好的局部性:一个人的所有属性被集中访问的同时,也被集中存储。3. 结构表达语义:简历与联系信息、教育经历、职业信息等隐含一对多的树状关系可以被 JSON 的树状结构明确表达出来。 | |
| 查询数据时的局部性 | 如果你同时需要文档中所有内容,把文档顺序存会效率比较高。但如果你只需要访问文档中的某些字段,则文档仍需要将文档全部加载出。 | 可以只查询自己所要的数据 |
| 实现 | 一般使用树实现 我们所常识的Mysql就是B+树实现 |
对于高度关联的数据集,使用文档型表达比较奇怪,使用关系型可以接受,使用图模型最自然。
文档模型中的Schema模型
| 数据模型 | 编程语言 | 性能 & 空间 | ||
|---|---|---|---|---|
| schema-on-read | 写入时不校验,而在读取时进行动态解析。 | 弱类型 | 动态,在运行时解析 | 读取时动态解析,性能较差。写入时无法确定类型,无法对齐空间利用率较差。 |
| schema-on-write | 写入时校验,数据对齐到 schema | 强类型 | 静态,编译时确定 | 性能和空间使用都较优。 |
文档模型典型的:schema-on-read 读取时检查类型, 关系模型典型的:schema-on-write 写入时检查类型, 慢慢的关系型也开始支持非简单类型了,支持Json格式的数据,并且可以建立索引
数据查询语言
| 声明式(declarative)语言 | 命令式(imperative)语言 | |
|---|---|---|
| 概念 | 描述控制逻辑而非执行流程 | 描述命令的执行过程,用一系列语句来不断改变状态 |
| 举例 | SQL,CSS,XSL | IMS,CODASYL,通用语言如 C,C++,JS |
| 抽象程度 | 高 | 低 |
| 解耦程度 | 与实现解耦。 可以持续优化查询引擎性能; | 与实现耦合较深。 |
| 解析执行 | 词法分析→ 语法分析 → 语义分析 生成执行计划→ 执行计划优化 | 词法分析→ 语法分析 → 语义分析 中间代码生成→ 代码优化 → 目标代码生成 |
| 多核并行 | 声明式更具多核潜力,给了更多运行时优化空间 | 命令式由于指定了代码执行顺序,编译时优化空间较小。 |
Q:相对声明式语言,命令式语言有什么优点?
- 当描述的目标变得复杂时,声明式表达能力不够。 ps:如果需要查询的数据集比较复杂时,可能要写要几页的SQl
- 实现命令式的语言往往不会和声明式那么泾渭分明,通过合理抽象,通过一些编程范式(函数式),可以让代码兼顾表达力和清晰性。
MapReduce查询:
图模型
- 文档模型的适用场景? 你的数据集中存在着大量一对多(one-to-many)的关系。
- 图模型的适用场景? 你的数据集中存在大量的多对多(many-to-many)的关系。
基本概念: 图数据模型的基本概念一般有三个**:点 边 ** 和附着于两者之上的属性。
常见的可以用图建模的场景: 社交图谱:人是点,follow关系是边,同时可以添加label表示他们之前的关系 互联网:网页是点,链接关系是边 路网:交通枢纽是点,铁路/公路是边 知识图谱:概念是点,关联关系是边
有多种对图的建模方式:
- 属性图(property graph):比较主流,如 Neo4j、Titan、InfiniteGraph
- 三元组(triple-store):如 Datomic、AllegroGraph
[属性图(PG,Property Graphs)]
| 点(vertices, nodes, entities) | 边(edges, relations, arcs) |
|---|---|
| 全局唯一 ID | 全局唯一 ID |
| 出边集合 | 起始点 |
| 入边集合 | 终止点 |
| 属性集(kv 对表示) | 属性集(kv 对表示) |
| 表示点类型的 type? | 表示边类型的 label |
- Q:有一个疑惑点,为什么书中对于 PG 点的定义中没有 Type ?
- 通过视频讲解在图中type标识类型其实在图中很重要
视频中阐述的一个观点是: 如果使用关系模型,在查询关联数据时需要大量的join,采用了笛卡尔乘积后,数据都会异常的庞大,哪怕采取了剪枝,数据集也还是很大,而图天然就适合数据聚合查询。
图是一种很灵活的建模方式:
- 任何两点间都可以插入边,没有任何模式限制。
- 对于任何顶点都可以高效(思考:如何高效?)找到其入边和出边,从而进行图遍历。
- 使用多种标签来标记不同类型边(关系)。
相对于关系型数据来说,可以在同一个图中保存异构类型的数据和关系,给了图极大的表达能力!
这种表达能力,根据图中的例子,包括:
- 对同样的概念,可以用不同结构表示。如不同国家的行政划分。
- 对同样的概念,可以用不同粒度表示。比如 Lucy 的现居住地和诞生地。
- 可以很自然的进行演化。
将异构的数据容纳在一张图中,可以通过图遍历,轻松完成关系型数据库中需要多次 Join 的操作。
书中举了一个例子,如果使用图查询的话,是怎样的查询语句?
找出所有从美国移居到欧洲的人名。
MATCH (person) -[:BORN_IN]-> () -[:WITHIN*0..]-> (us:Location {name:'United States'}),
(person) -[:LIVES_IN]-> () -[:WITHIN*0..]-> (eu:Location {name:'Europe'}) RETURN person.name
可以使用SQL进行图查询吗? 可以的,但是相当冗余和笨拙
Triple-Stores,可以理解为三元组存储,即用三元组存储图。
其含义如下:
| Subject | 对应图中的一个点 |
|---|---|
| Object | 1. 一个原子数据,如 string 或者 number。 2. 另一个 Subject。 |
| Predicate | 1. 如果 Object 是原子数据,则 <Predicate, Object> 对应点附带的 KV 对。 2. 如果 Object 是另一个 Object,则 Predicate 对应图中的边。 |
语义网
RDF数据模型
SPARQL查询语言
[图模型和网络模型]
图模型是网络模型旧瓶装新酒吗?
否,他们在很多重要的方面都不一样。
| 模型 | 图模型(Graph Model) | 网络模型(Network Model) |
|---|---|---|
| 连接方式 | 任意两个点之间都有可以有边 | 指定了嵌套约束 |
| 记录查找 | 1. 使用全局 ID 2. 使用属性索引。 3. 使用图遍历。 | 只能使用路径查询 |
| 有序性 | 点和边都是无序的 | 记录的孩子们是有序集合,在插入时需要考虑维持有序的开销 |
| 查询语言 | 即可命令式,也可以声明式 | 命令式的 |