大多数应用程序是经过一层一层叠加数据模型来构建的。每一层都面临的关键问题是:如何将其用下一层来表示?例如:数据库
复杂的应用程序可能会有更多的中间层,例如基于API来构建上层API,可是基本思想相同:每层都经过提供一个简洁的数据模型来隐藏下层的复杂性。有许多不一样类型的数据模型,每种数据模型都有其最佳使用的若干假设。编程
如今最著名的数据模型多是SQL,它基于Edgar Codd于1970年提出的关系模型:数据被组织成关系(relations),在SQL中称为表(table),其中每一个关系都是元组(tuples)的无序集合(在SQL中称为行)。segmentfault
采用NoSQL数据库有这样几个驱动因素,包括:网络
如今大多数应用开发都采用面向对象的编程语言,因为兼容性问题,广泛对SQL数据模型存在抱怨:若是数据存储在关系表中,那么应用层代码中的对象与表、行和列的数据库模型之间须要一个笨拙的转换层。数据结构
例如,如何在关系模式中表示简历。整个简历能够经过惟一的标识符user_id来标识。像first_name和last_name这样的字段在每一个用户中只出现一次,因此能够将其建模为users表中的列。然而,大多数人在他们的职业中有一个以上的工做,而且可能有多个教育阶段和任意数量的联系信息。用户与这些项目之间存在一对多的关系,能够用多种方式来表示:并发
对于像简历这样的数据结构,它主要是一个自包含的文档(document),所以用JSON表示很是合适,参见以下示例。与XML相比,JSON的吸引力在于它更简单。面向文档的数据库(如MongoDB、 RethinkDB、CouchDB和Espresso)都支持该数据模型。编程语言
{ "user_id": 251, "first_name" : "Bill", "last_name" : "Gates", "summary": "Co-chair of the Bill & Melinda Gates... Active blogger.", "region_id" : "us:91", "industry_id" : 131, "photo_url": "/p/7/000/253/05b/308dd6e.jpg", "positions" : [ {"job_title":"Co-chair", "organization":"Bill 8 Melinda Gates Foundation"}, {"job_title":"Co-founder,Chairman", "organization" : "Microsoft"} ], "education":[ {"school_name":"Harvard University","start" : 1973,"end": 1975}, {"school_name":"Lakeside School,Seattle", "start": null,"end": null} ], "contact_info": { "blog": "http: /lthegatesnotes.com", "twitter" :"http: //twitter.com/BillGates" } }
在上面的示例中,region_id和industry_id定义为ID,而不是纯文本字符串形式,例如"Greater Seattle Area"和"Philanthropy"。为何这样作呢?ide
若是用户界面是能够输入地区或行业的自由文本字段,则将其存储为纯文本字符串更有意义。可是,拥有地理区域和行业的标准化列表,并让用户从下拉列表或自动填充器中进行选择会更有优点,这样:性能
使用ID的好处是,由于它对人类没有任何直接意义,因此永远不须要直接改变:即便ID标识的信息发生了变化,它也能够保持不变。任何对人类有意义的东西均可能在未来某个时刻发生变动。若是这些信息被复制,那么全部的冗余副本也都须要更新。这会致使更多写人开销,而且存在数据不一致的风险(信息的一些副本被更新,而其余副本未更新)。消除这种重复正是数据库规范化的核心思想。大数据
然而这种数据规范化须要表达多对一的关系(许多人生活在同一地区,许多人在同一行业工做),这并非很适合文档模型。
文档数据库是某种方式的层次模型:即在其父记录中保存了嵌套记录(一对多关系,如前面例子中的positions、education和contact_info),而不是存储在单独的表中。
可是,在表示多对一和多对多的关系时,关系数据库和文档数据库并无根本的不一样:在这两种状况下,相关项都由惟一的标识符引用,该标识符在关系模型中被称为外键,在文档模型中被称为文档引用。标识符能够查询时经过联结操做或相关后续查询来解析。
对于关系数据库,因为支持联结操做,能够很方便地经过ID来引用其余表中的行。而在文档数据库中,一对多的树状结构不须要联结,支持联结一般也很弱。
即便应用程序的初始版本很是适合采用无联结的文档模型,但随着应用支持愈来愈多的功能,数据也变得更加互联一体化。
在比较关系数据库与文档数据库时,须要考虑不少方面的差别,包括它们的容错性和并发处理,这里只关注数据模型中的差别。
支持文档数据模型的主要论点是模式灵活性,因为局部性而带来较好的性能,对于某些应用来讲,它更接近于应用程序所使用的数据结构。关系模型则强在联结操做、多对一和多对多关系更简洁的表达上,与文档模型抗衡。
若是应用数据具备相似文档的结构(即一对多关系树,一般一次加载整个树),那么使用文档模型更为合适。而关系型模型则倾向于某种数据分解,它把文档结构分解为多个表,有可能使得模式更为笨重,以及没必要要的应用代码复杂化。
文档模型也有必定的局限性:例如,不能直接引用文档中的嵌套项,而须要说“用户251的职位列表中的第二项”(很是相似于层次模型中的访问路径)。然而,只要文档嵌套不太深,这一般不是问题。
在文档数据库中,对联结的支持不足是不是问题取决于应用程序。例如,在使用文档数据库记录事件发生时间的应用分析程序中,可能永远不须要多对多关系。
可是,若是应用程序确实使用了多对多关系,那么文档模型就变得不太吸引人。能够经过反规范化来减小对联结的需求,可是应用程序代码须要作额外的工做来保持非规范化数据的一致性。经过向数据库发出多个请求,能够在应用程序代码中模拟联结,可是这也将应用程序变得复杂,而且一般比数据库内的专用代码执行的联结慢。在这些状况下,使用文档模型会致使应用程序代码更复杂、性能更差。
一般没法一律而论哪一种数据模型的应用代码更简单。这主要取决于数据项之间的关系类型。对于高度关联的数据,文档模型不太适合,关系模型能够胜任,而图模型则是最为天然的。
大多数文档数据库,以及关系数据库中的JSON支持,都不会对文档中的数据强制执行任何模式验证。关系数据库中的XML一般支持带有可选的模式验证功能。没有模式验证意味着能够将任意的键-值添加到文档中,而且在读取时,客户端没法保证文档可能包含哪些字段。
文档数据库有时被称为无模式,但这具备误导性,由于读数据的代码一般采用某种结构于是存在某种隐形模式,而不是由数据库强制执行。更准确的术语应该是读时模式(数据的结构是隐式的,只有在读取时才解释),与写时模式(关系数据库的一种传统方法,模式是显式的,而且数据库确保数据写入时都必须遵循)相对应。
读时模式相似编程语言中的动态(运行时)类型检查,而写时模式相似于静态(编译时)类型检查。正如静态与动态类型检查的支持者对于它们的优缺点存在很大的争议同样,数据库的模式执行也是一个有争议的话题,一般没有明确正确或错误的答案。
若是集合中的项因为某种缘由(例如数据异构),并不都具备相同的结构,例如:
在这些状况下,模式带来的损害大于它所能提供的帮助,无模式文档多是更天然的数据模型。可是,当全部记录都有相同结构时,模式则是记录和确保这种结构的有效机制。
文档一般存储为编码为JSON、XML或其二进制变体(如MongoDB的BSON)的连续字符串。若是应用程序须要频繁访问整个文档,则存储局部性具备性能优点。若是数据被划分在多个表中(关系模型),则须要进行屡次索引查找来检索全部数据,中间可能须要更多的磁盘I/O并花费更多的时间。
局部性优点仅适用须要同时访问文档大部份内容的场景。因为数据库一般会加载整个文档,若是应用只是访问其中的一小部分,则对于大型文档数据来说就有些浪费。对文档进行更新时,一般会重写整个文档,而只有修改量不改变源文档大小时,原地覆盖更新才更有效。所以,一般建议文档应该尽可能小且避免写人时增长文档大小。这些性能方面的不利因素大大限制了文档数据库的适用场景。
随着时间的推移,彷佛关系数据库与文档数据库变得愈来愈相近,或许这是一件好事:数据模型能够相互补充。若是数据库可以很好处理文档类数据,还能对其执行关系查询,那么应用程序可使用最符合其需求的功能的组合。
融合关系模型与文档模型是将来数据库发展的一条很好的途径。
当关系模型是最初被引入时,就包含了查询数据的新方法:SQL是一种声明式查询语言,而IMS和CODASYL则是命令式。这种差异意味着什么呢?
命令式语言告诉计算机以特定顺序执行某些操做。你彻底能够推理整个过程,逐行遍历代码、评估相关条件、更新对应的变量,并决定是否再循环一遍。
而对于声明式查询语言(如SQL或关系代数),则只需指定所需的数据模式,结果需知足什么条件,以及如何转换数据(例如,排序、分组和聚合),而不需指明如何实现这一目标。数据库系统的查询优化器会决定采用哪些索引和联结,以及用何种顺序来执行查询的各个语句。