Design Data-Intensive Applications 读书笔记 二 数据模型

本文探讨了数据模型在软件开发中的重要性,重点关注了关联模型(SQL)与文档模型(NoSQL)的差异。关联模型通过关系来组织数据,而NoSQL数据库则提供更好的扩展性和动态数据模型。两者之间的不匹配主要体现在对象关系映射的挑战,如简历存储的多种表示方式。文章还讨论了一对多和多对多关系的处理,以及两种模型在处理这些关系时的优缺点。最后,文章提到了现代数据库的发展趋势,如SQL数据库对JSON的支持,以及NoSQL数据库对JOIN操作的改进,表明两种模型正逐渐融合以满足多样化的需求。
摘要由CSDN通过智能技术生成

第二章 数据模型和查询语句

数据模型或许是软件开发中最重要的部分,它们不仅规定了软件如何被编写,也影响了我们如何思考我们要解决的问题。

绝大部分应用都是分层来构造数据模型,每一层的关键问题是:根据底层如何表示这一层?

例如:

1、作为应用开发者,你会查看现实世界(人,公司,货物,行动,现金流,感应器等等),然后将相关事物和数据结构建模。这些结构是你的应用特有的。

2、当你想存储这些数据结构,你会用一些普遍的数据模型来表示他们,例如JSON或XML,关联表格或者图形模型

3、数据库工程师决定如何用内存、磁盘或者网络来表示JSON/XML/关联/图形数据。展示方法允许数据查询、搜索和处理

3、硬件工程师决定如何更具电器设备来表示二进制数据

每一层都通过提供更清晰的数据模型隐藏了下一层的复杂性。

关联模型与文档模型

目前最著名的数据模型可能就是SQL,基于关联模型。Edgar Codd于1970年提出,数据通过关联来组织,每个关系都是元组的无序集合(SQL中的行)

关联型数据库的根基在于商业数据处理,那是上世纪60,70年代大型机处理的工作。那些用户案例在今天看来很普遍,典型的事务处理(存储订单,预定航线,股票交易)和批量处理(用户开发票,开工资,作报告)。

其他数据库在那个年代强迫开发者思考很多数据在数据库里的内在表示。关联型模型的目标就是将细节的实现隐藏在一个干净的接口后面。

到目前关联型数据库占据了统治地位。

NoSQL数据库的诞生

最近NoSQL数据库试图挑战关系型数据库的统治地位。“NoSQL”这个名字其实是遗憾的,因为它没有指代任何特定的技术,它最初是Twitter给一个开源会议打的标签,会议讨论的是一个开源的,分布式,非关系型数据库。然而,这个名字不胫而走,传播广泛。相当一部分数据库开始关联上NoSQL,它也从根本上被重新解释为“不止于SQL”。

使用NoSQL数据库的动力有以下几点:

1、比关系型数据库更好的扩展性,更高的写入性能。

2、使用超过商业数据库产品的免费开源产品

3、关系模型没法很好支持的特定查询操作

4、对更加具有动态表现力的数据模型的追求,关系模型在无约束性上的限制。

不同的应用有不同的需求。可以预见在未来关系型数据库会与非关系型数据库共存。

对象-关系的不匹配

今天的绝大多数应用使用面向对象语言开发,这导致人们普遍批评关系模型:如果数据存到关系表格中,在对象和数据模型的表、行、列之间需要一个很尴尬的转换层。模型之间的这种断裂有时称为:不匹配阻抗。对象关系映射(ORM)框架减少了传输层需要的样板代码,但是他们不可能完全隐藏两个模型之间的差别。

比如要存储一个简历。这个简历可以用一个标志符鉴别,user_id。first_name和last_name这些字段每个人只会有一个,所以它们可以作为user表的列。但是大多数人又不止一段工作经历,而且人们有着很多教育经历和联系方式,用户有很多一对多的关系,它们可以用一下方法表示:

传统SQL模型普遍是将位置,教育和联系信息分别存在独立的表格中,然后用外键关联至users表格。

最近的SQL标准添加了对结构化数据类型和XML数据的支持,它允许单行存储有多个值的数据,并且支持在文档中查询和建索引。

第三个选项是将工作,教育和联系方式转换成JSON和XML文档,然后存到数据库的文本列汇总,然后让应用解析。在这步中,你没法使用数据库在编码的列中查询。

不少开发者觉得JSON模型减少了系统和存储层之间的不匹配阻抗。但是JSON作为数据编码格式也有问题。

JSON表示法比多表格模式有更好的定位特性。如果你想在关系模型中找到一个资源,你要么做多次查询,要么做关联查询。在JSON模式中,所有相关信息都在一个地方,一个查询搞定。JSON能很明确的表达树形结构。

 

一对多和多对多关系

在简历例子中,为什么region_id,industry_id都是给出ID,而不是文本。如果用户界面有文本框用作输入,那么将其当做文本存起来是有意义的。但是更好的方法是提供标准的地理区域和企业列表,让用户自己选。有以下好处:

维护拼写风格统一;避免歧义(重名城市);更新方便,名字至存储在一个地方,很容易更新;支持本地化,当网站翻译成其他语言,标准列表也可以本地化,地区和企业可以用用户的语言显示;更好的查询,地区列表都可以进行编码。

存储ID还是文本是副本问题。当你使用ID,对于人来说有用的信息只会存储在一个地方。当你直接存储文本,你重复存储了对人来说有用的信息。

使用ID的好处是,它对人没有意义,它不需要修改,即使它对应的信息有改动,ID也不需要变。任何对于人来说有意义的信息在将来都可能改变,如果那些信息有副本,那么所有的副本都需要更新。这会导致写入量的增长和不一致风险。移除类似的副本就是数据库中的标准化。

但是标准化需要多对一的关系,这和文档模型契合度不高。在关系型数据库中,因为join操作很容易,才考其他表格的id很容易。在文档数据库中,join操作不需要一对多关系树,而且对join操作的支持很弱。

如果数据库自身不支持join操作,你需要在应用中使用多次查询来模拟join操作。但是即便应用完全不需要join操作,随着加入数据变多,数据还是会倾向于缠在一起。

文档数据库在重复历史吗

多对多关系和join操作能在关系型数据库很自然的使用,文档型数据库和NoSQL重新开启了如何最好地表示类似关系的争论,事实上类似的争论最早可以追溯到最初的计算机数据库系统。

20世纪70年代最受欢迎的商业数据库是IBM的“信息管理系统”(IMS),阿波罗项目中用作存储股票,1968年用作存货管理。IMS系统使用简单的数据结构,层次数据模型,非常类似于文档数据库的JSON模型。它使用记录嵌套记录形成的数据结构来表示数据。

就像文档数据库,IMS适用于一对多关系,但是它很难处理多对多关系,而且不支持join操作。开发者不得不决定是重复数据还是手动处理一条记录至另一条记录的参考。这些在60,70年代出现的问题出现在了今天的文档数据库。

为解决层次模型的缺陷,提出了很多方法,最有潜力的就是两个,一是关联模型(今天成为了SQL,然后统治世界),二是网络模型(起初有大批拥趸,但是后来销声匿迹)。70年代,两派的争论一直在持续。因为两个模型解决的问题很重大,现在可以回顾这场争论。

网络模型:网络模型是一场会议(CODASYL)提出的,然后被多个数据库实现,被称为CODASYL模型。CODASYL模型是层次模型的衍生;在层次模型的树结构中,每个记录只有一个父节点,在网络模型中,每个记录有多个父节点。网络模型中每个记录的连接不是通过外键,更像是指针。获取记录的唯一方法就是从根节点开始沿着链接形成的链条直到目标节点,这被称为访问路径。在最简单的情况下,一个访问路径就像是遍历链表,你只有访问到你想要的节点才能获得数据。但是在多对多关联的情况下,多个访问路径可能指向同一个节点,使用网络模型的开发者不得不在脑中维护所有的访问路径。

CODASYL模型中的查询时是通过移动游标遍历在访问路径上节点来实现的,如果一个节点有多个父节点,应用就得跟踪所有的连接,连CODASYL制作组成员都说这像是在n维数据空间中导航。因而查询和更新的代码非常复杂和僵化,在网络模型和层次模型中,如果你没有你想要的数据的路径,那么你就处在困境。你可以更改访问路径,但是你不得不遍历大量的查询代码来掌控你新修改的路径,这使得修改应用的数据模型很困难。

关联模型:关联模型做的就是将所有的数据展开,一个关联就是一个元组的集合。没有嵌套结构,没有复杂的访问路径,你可以浏览表中任意的数据,选取那些符合任意条件的部分。你可以将特定列作为键值来获取特定列,你可以插入新的一行而不用担心其他表格的外键关联。

在关联数据库中,查询可以自动选择查询语句的执行顺序,以及使用哪些索引,这些选择被称为“访问路径”,不同的是它使用数据库的查询优化器来实现而不是应用开发者,你不需要修改你的代码来使用一个新的索引,这使得很容易能添加新的属性。

关联数据库的查询优化器非常复杂,它需要多年的研究和开发;但是关键的是你只需要建立一个查询优化器,然后所有使用这个数据库的应用都能受益。如果你没有查询优化器,那么手动给特定查询语句编写代码比写一个普遍适用的优化器简单,但是普遍适用的解决方案胜在长期。

对比文档数据库:文档数据库在一个方面回到了层次模型:在父节点中存储嵌套的记录,而不是使用单独的表存储。但是在表示多对一和多对多关系时,关联性数据库和文档型数据库本质上没有不同:在两种情况下,关联的记录使用特定标志来指代,在关联模型中称为外键,在文档模型中称为文档参考。标志在读取时使用join操作或者后续查询来解析。今天文档模型没有走CODASYL的老路。

今天的关系模型和文档模型

在比较关系型数据库和文档数据库时,要考虑很多不同,比如故障容忍特性,并发,这章只考虑数据模型的不同。

使用数据模型的主要好处是模式弹性,更好的定位性能,以及更贴近某些应用的数据结构。

哪种数据模型能简化代码?:如果你的应用的数据有着文档结构(比如一对多关系,并且经常是一次加载整个树),那么倾向使用文档模型。关联技术的碎片化,将一个类似文档的结构分成不同的表,会导致笨重的模式和不必要的复杂代码。

文档模型局限:不能直接在一个文档里指定嵌套的项目,你需要指定“251用户位置列表的第二个项目”(就像层级模型的访问路径)。但是,只要文档嵌套不是很深,不会一直是个难题。

根据应用类型,文档模型很难支持join操作,这可能成为一个问题。比如,多对多关系可能在一个使用文档数据库来记录哪些事件在什么时候发生的分析系统中永远都不需要。但是,如果你的应用确实需要使用多对多关系,不要用文档模型。会需要额外的工作来满足join操作的需求。在应用中模仿join操作会给系统带来额外的复杂性,使用数据库的join功能会降低数据库性能。这种情况下使用文档模型会导致额外的复杂度和更糟的性能。

很难说哪种数据模型能简化应用,这取决于现存的数据对象的关系。对于高度联结的数据,文档模型很不适合,关联模型可以接受,图式模型最自然。

文档模型的模式弹性

大部分文档数据库和支持JSON格式的关系数据库,都没有强制文档里数据的模式。支持XML的关系数据库提供模式可选。没有模式意味着可以往文档中任意加入键值对。而且读取时,客户端没有保证文档中会含有那些字段。文档数据库有时被称作“无模式”,这不准确,因为代码再读数据时会设定某些结构,也就是隐含的模式,数据库并不对此作出强制。更准确的说法是,“读时模式”(数据结构是隐含的,只在数据读取的时候解析),与之相对的是“写时模式”(关系型数据库的传统做法,模式是确定的,数据库确保所有的数据在写入时都遵从它。)。读时模式类似于动态检测类型,写时模式类似于静态检测,他们各有优点,没有标准的对或错。

两种方法的区别在修改数据时尤为突出。比如之前你将用户名字存在一个字段里,现在要将姓和名分开,在文档数据库里你只需要在新文档里分开写入,同时在代码里处理读取旧文档的情况。但是在“静态类型”的数据库中,你需要每行都作出迁移操作。模式更改是出了名的慢,而且需要停工期。大多数数据库执行ALTER TABLE操作只需要几十毫秒,但是Mysql数据库是个例外,在执行ALTER TABLE时会复制整个表格。这意味着改动大的表格时会有几十分钟甚至小时的停工期。运行UPDATE操作也比其他数据库慢,因为每行都需要被重写。如果这是不可接受的,那么可以将“名”这个字段留空,读取时在解析出来,这类似于文档数据库。

读时模式对于处理因为某些原因,集合中的项目没有相同的结构这种情况很有优势。因为1、对象有不同的类别,将每个对象放进它的表格很不现实。2、数据结构由其他你无法控制的系统来决定,而且随时会变。在这种场景下,模式的弊大于利。无模式的文档模型更加适合。但是,如果是所有需要所有记录有相同结构的情况,那么模式是确保结构的有力工具。

数据查询定位

一个文档经常会被存储为一串连续的字符串,用JSON,XML或者其他二进制格式保存。如果你的应用经常需要获取整个文档,那么这种存储定位很有优势。如果数据切分为多个表格,需要遍历多个表格来查找多个索引,或产生更多的磁盘检索操作和时间。

定位优势只有在你需要大块的文档时才有用。数据库一般需要加载整个文档,即便你只需要其中的一小部分。对于大文档,这是显著浪费。更新时,整个文档需要被重写,只有不改变文档编码的操作才能很快执行。因为这些原因,建议使用相对小的文档,避免写入,造成文档体积增大。这些表现严重限制了文档数据库的使用范围。

需要指出的是,打包关联的表格用作定位不是文档数据库独有的。比如谷歌的Spanner数据库在关系数据模型中提供相同的定位特性,它允许模式声明一个表的行应该插入(嵌套)进一个父表中。Oracle类似,使用特性multi-table index cluster tables 。

总述文档和关系型数据库

大多数关系型数据库(除了MySQL)自从mid-2000后开始支持XML ,允许本地修改XML文档,在XML文档内部做索引和查询,能够让应用像使用文档数据库一样工作。9.3版本之后PostgreSql,5.7版本之后的MySQL开始支持JSON文档,给Web API提供JSON,好像其他的关系型数据库会跟随它们的脚步。

文档数据库这边,RethinkDB开始支持JOIN操作,MongoDB也开始解决数据库引用(高效处理client端的join操作)。

看起来关系型数据库和文档数据库越来越相似了。这是好事情,数据模型可以互补。如果一个数据库能够处理类似文档的数据能够操作关联性查询,那么应用就能结合它们的特性,最好的满足需求。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值