目录

阅读:DDIA 第二章

第二章:数据模型与查询语言

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

多数应用使用层层叠加的数据模型构建。

  1. 现实世界:采用对象或数据结构,以及操控那些数据结构的 API 来进行建模
  2. 存储数据结构,通用数据模型如 JSON 或 XML 文档、关系数据库中的表或图模型。
  3. 内存、磁盘或网络上的字节来表示 JSON / XML/ 关系 / 图数据。这类表示形式使数据有可能以各种方式来查询,搜索,操纵和处理。
  4. 硬件工程师已经想出了使用电流、光脉冲、磁场或者其他东西来表示字节的方法

一个复杂的应用程序可能会有更多的中间层次,比如基于 API 的 API,不过基本思想仍然是一样的:每个层都通过提供一个明确的数据模型来隐藏更低层次中的复杂性。这些抽象允许不同的人群有效地协作(例如数据库厂商的工程师和使用数据库的应用程序开发人员)。

选择一个适合的数据模型是非常重要的。

数据模型到底是什么?用来描述、组织、操作数据,对现实进行抽象?

本章介绍用于数据存储和查询的通用数据模型(比较关系模型、文档模型、图数据模型)和查询语言,以及存储引擎和数据模型如何实现的

关系模型与文档模型

现在最著名的数据模型可能是 SQL。它基于 Edgar Codd 在 1970 年提出的关系模型:数据被组织成 关系(SQL 中称作 表),其中每个关系是 元组(SQL 中称作 行) 的无序集合。

关系模型曾是一个理论性的提议,当时很多人都怀疑是否能够有效实现它。然而到了 20 世纪 80 年代中期,关系数据库管理系统(RDBMSes)和 SQL 已成为大多数人们存储和查询某些常规结构的数据的首选工具

关系数据库起源于·,在 20 世纪 60 年代和 70 年代用大型计算机来执行。从今天的角度来看,那些用例显得很平常:典型的 事务处理(将销售或银行交易,航空公司预订,库存管理信息记录在库)和 批处理(客户发票,工资单,报告)

当时的其他数据库迫使应用程序开发人员必须考虑数据库内部的数据表示形式。关系模型致力于将上述实现细节隐藏在更简洁的接口之后

多年来,在数据存储查询方面存在着许多相互竞争的方法。在 20 世纪 70 年代和 80 年代初,网状模型(network model)和层次模型(hierarchical model)曾是主要的选择,但关系模型(relational model)随后占据了主导地位。对象数据库在 20 世纪 80 年代末和 90 年代初来了又去。XML 数据库在二十一世纪初出现,但只有小众采用过。关系模型的每个竞争者都在其时代产生了大量的炒作,但从来没有持续。

随着电脑越来越强大和互联,它们开始用于日益多样化的目的。关系数据库非常成功地被推广到业务数据处理的原始范围之外更为广泛的用例上。你今天在网上看到的大部分内容依旧是由关系数据库来提供支持,无论是在线发布,讨论,社交网络,电子商务,游戏,软件即服务生产力应用程序等等内容。

NoSQL 的诞生

现在 - 2010 年代,NoSQL 开始了最新一轮尝试,试图推翻关系模型的统治地位。“NoSQL” 这个名字让人遗憾,因为实际上它并没有涉及到任何特定的技术。最初它只是作为一个醒目的 Twitter 标签,用在 2009 年一个关于分布式,非关系数据库上的开源聚会上。无论如何,这个术语触动了某些神经,并迅速在网络创业社区内外传播开来。好些有趣的数据库系统现在都与 #NoSQL 标签相关联,并且 NoSQL 被追溯性地重新解释为 不仅是 SQL(Not Only SQL)

采用 NoSQL 数据库的背后有几个驱动因素,其中包括:

  • 需要比关系数据库更好的可伸缩性,包括非常大的数据集或非常高的写入吞吐量
  • 相比商业数据库产品,免费和开源软件更受偏爱
  • 关系模型不能很好地支持一些特殊的查询操作
  • 受挫于关系模型的限制性,渴望一种更具多动态性与表现力的数据模型

不同的应用程序有不同的需求,一个用例的最佳技术选择可能不同于另一个用例的最佳技术选择。因此,在可预见的未来,关系数据库似乎可能会继续与各种非关系数据库一起使用 - 这种想法有时也被称为 混合持久化(polyglot persistence)。

对象关系不匹配

目前大多数应用程序开发都使用面向对象的编程语言来开发,这导致了对 SQL 数据模型的普遍批评:如果数据存储在关系表中,那么需要一个笨拙的转换层,处于应用程序代码中的对象和表,行,列的数据库模型之间。模型之间的不连贯有时被称为 阻抗不匹配(impedance mismatch)。

像 ActiveRecord 和 Hibernate 这样的 对象关系映射(ORM object-relational mapping) 框架可以减少这个转换层所需的样板代码的数量,但是它们不能完全隐藏这两个模型之间的差异。

ORM 框架和手写 SQL 应该怎么选择?

例如,图 2-1 展示了如何在关系模式中表示简历(一个 LinkedIn 简介)。整个简介可以通过一个唯一的标识符 user_id 来标识。像 first_name 和 last_name 这样的字段每个用户只出现一次,所以可以在 User 表上将其建模为列。但是,大多数人在职业生涯中拥有多于一份的工作,人们可能有不同样的教育阶段和任意数量的联系信息。从用户到这些项目之间存在一对多的关系,可以用多种方式来表示:

  • 传统 SQL 模型(SQL:1999 之前)中,最常见的规范化表示形式是将职位,教育和联系信息放在单独的表中,对 User 表提供外键引用
  • 后续的 SQL 标准增加了对结构化数据类型和 XML 数据的支持;这允许将多值数据存储在单行内,并支持在这些文档内查询和索引。这些功能在 Oracle,IBM DB2,MS SQL Server 和 PostgreSQL 中都有不同程度的支持。JSON 数据类型也得到多个数据库的支持,包括 IBM DB2,MySQL 和 PostgreSQL。
  • 第三种选择是将职业,教育和联系信息编码为 JSON 或 XML 文档,将其存储在数据库的文本列中,并让应用程序解析其结构和内容。这种配置下,通常不能使用数据库来查询该编码列中的值。

对于一个像简历这样自包含文档的数据结构而言,JSON 表示是非常合适的,JSON 比 XML 更简单。面向文档的数据库(如 MongoDB,RethinkDB,CouchDB 和 Espresso)支持这种数据模型。

有一些开发人员认为 JSON 模型减少了应用程序代码和存储层之间的阻抗不匹配。不过,正如我们将在 第四章 中看到的那样,JSON 作为数据编码格式也存在问题

JSON 表示比 图 2-1 中的多表模式具有更好的 局部性(locality)。如果在前面的关系型示例中获取简介,那需要执行多个查询(通过 user_id 查询每个表),或者在 User 表与其下属表之间混乱地执行多路连接。而在 JSON 表示中,所有相关信息都在同一个地方,一个查询就足够了。

从用户简介文件到用户职位,教育历史和联系信息,这种一对多关系隐含了数据中的一个树状结构,而 JSON 表示使得这个树状结构变得明确

多对一和多对多的关系

region_idindustry_id 是以 ID,而不是纯字符串 “Greater Seattle Area” 和 “Philanthropy” 的形式给出的。为什么?

如果用户界面用一个自由文本字段来输入区域和行业,那么将他们存储为纯文本字符串是合理的。另一方式是给出地理区域和行业的标准化的列表,并让用户从下拉列表或自动填充器中进行选择,其优势如下:

  • 各个简介之间样式和拼写统一
  • 避免歧义(例如,如果有几个同名的城市)
  • 易于更新 —— 名称只存储在一个地方,如果需要更改(例如,由于政治事件而改变城市名称),很容易进行全面更新。
  • 本地化支持 —— 当网站翻译成其他语言时,标准化的列表可以被本地化,使得地区和行业可以使用用户的语言来显示
  • 更好的搜索 —— 例如,搜索华盛顿州的慈善家就会匹配这份简介,因为地区列表可以编码记录西雅图在华盛顿这一事实(从 “Greater Seattle Area” 这个字符串中看不出来)