分布式系统笔记(二) 数据库的数据模型和 Query Language

数据模型是指软件对于所处理数据的抽象, 它对软件的设计有很大影响:数据如何组织,某个操作如何实现,有些操作很快有些操作很慢,有些操作很自然有些操作很奇怪,这些问题都与数据模型有很大关系。(想想上一篇 Twitter 的例子

在真实的系统中,往往存在多种数据模型。对数据库而言,最常见的数据模型有 Relational Model 和 Document Model。除此之外还有一些特殊的模型。

Relational Model

在 Relational Model 中,数据被组织成若干个 Relation(MySQL 中的 table),每个 Relation 包含了若干个无序的 tuple(MySQL 中的 row)。在很多关系型数据库中,每个 Relation 内部其实是有序的。如 Inno DB 中数据是以主键的大小顺序存储的,在某些特殊情况下,还会为某个 table 生成一个自增整数作为主键

关系模型的优点主要有简单,高效,灵活,历史上,关系模型曾经一统江山长达数十年, 即使在今天,关系模型仍然占据极为重要的地位,以至于它是很多人心中“默认”的数据模型。 它的缺点之一是,当数据间的关系高度嵌套(树形数据)时,关系模型就显得不那么自然。这造成了一种叫 Object-Relation Mismatch 的问题, 某些情况下我们不得不做多次 join 才能得到某个对象中的一条数据。 某些关系型数据库正在尝试引入数组类型,嵌套类型(JSON 类型)来减轻这些问题,但在树状/网状数据的处理上,不得不说关系模型有其天生的弱势。

提到数据模型的时候,很多文章(包括 Designing Data Intensive Applications)都会从追忆 IMS 时代开始,介绍关系模型的辉煌历史,介绍传说中的”Great Debate”,有兴趣的同学可以找相关资料了解一下。

Document Model

文档模型现在是 NoSQL 里发展的最好的一类了。跟关系模型相比,文档模型的 schema 更加灵活,支持嵌套,数组等数据结构, 对某些应用来说更加”自然”。

文档模型在存储高度嵌套的树状数据时较关系模型更加自然。 想象你正要查询”小明高一期中考试数学试卷最后一道选择题的分数”,在关系模型中,你需要维护若干个 Relation, 并在查询的时候在不同的 Relation 之间进行 join 操作;一个替代方案是将考生,科目,学期,试卷 id 等大量信息压入一个 tuple 以减少 join 次数,但这样一来会浪费大量的存储空间。而在文档模型中,你只需要将每个学生高中所有大考的小题成绩存在一起, 查询时直接查找嵌套其中的一条数据即可(这一步可以通过偏移量在极短的时间内得到)。

文档模型较关系模型有更高的灵活性。在关系型数据库中对列的修改往往要慎之又慎, 如何保证在分布式环境下对列的增删不会影响兼容性并不是一个简单的话题。相比之下文档模型 schema free 的存储方式更加适合快速迭代的系统。除此之外,在现实中很多数据是异构的(如各国行政区划,互联网公司的用户资料等), 存储这种数据时无疑 schema free 的方式更加自然。

另外一个差异是数据的存储方式,文档模型的 locality 跟关系模型完全不同,关系模型是主键相近的在一起, 文档模型是按树状存的,这个差异在不同的应用中各有胜场。

文档模型相对关系模型的缺点也显而易见,一是在文档模型中加索引会影响其灵活性,二是 schema free 并不意味着没有 schema, 而是没有全局的 schema,每个数据项实际上都会存一个 schema 用于快速定位内部的元素,三是文档模型对 join 的支持较差。 除此之外,据我观察很多文档模型的数据库对 transaction 的支持和对查询的优化都不如成熟的关系型数据库, 但我不确定这是否是文档模型的固有缺陷。另外,有些批评认为 schema free 的存储方式不利于错误的排查, 也容易导致因为类型转换导致的 bug,这些问题确实存在,然而解决这些问题是否是数据库的责任有待商榷, 很多热门数据库也有对应的外部工具来避免这个问题。

Other Data Model

无论是关系模型还是文档模型,都不容易处理多对多的关系。为了处理这类数据,有些数据库采用了图模型。 除此之外,某些专门的数据库(如 DNA 数据库和搜索引擎)还会采用其他的特殊模型。

某些数据库正在尝试使用一些混合的策略:MySQL 在 5.7 中引入 JSON 数据类型,RethinkDB 则开始在查询中支持 relational-like 的 join。这种混合模型可以在一定程度上取长补短, 方便开发者的使用。

Query Language

Query Language 一般分成两种,一种是定义式的(如 MySQL),一种是命令式的(如 CODASYL), 现在比较流行的数据库大部分采用的是定义式查询语言。定义式查询语言的优点是简单,容易编写,也有利于数据库做查询优化。 命令式的优点是灵活,在实现复杂功能的时候更有优势。有些数据库通过插件等方式支持命令式的查询语言, 在进行深度定制的查询时有一定作用。很多MapReduce实现采用的查询语言也是命令式的。