【分布式—基础】数据模型与查询语言

大多数应用程序是通过一层一层叠加数据模型来构建的。每一层都面临的关键问题是:如何将其用下一层来表示?例如:

  1. 作为一名应用程序开发人员,观测现实世界(其中包括人员、组织、货物、行为、资金流动、传感器等),通过对象或数据结构,以及操作这些数据结构的API来对其建模。这些数据结构往往特定于该应用。
  2. 当需要存储这些数据结构时,可以采用通用数据模型(例如JSON或XML文档、关系数据库中的表或图模型)来表示。
  3. 数据库工程师接着决定用何种内存、磁盘或网络的字节格式来表示上述JSON/XML/关系/图形数据。数据表示需要支持多种方式的查询、搜索、操作和处理数据。
  4. 在更下一层,硬件工程师则需要考虑用电流、光脉冲、磁场等来表示字节。

复杂的应用程序可能会有更多的中间层,例如基于API来构建上层API,但是基本思想相同:每层都通过提供一个简洁的数据模型来隐藏下层的复杂性。有许多不同类型的数据模型,每种数据模型都有其最佳使用的若干假设。

关系模型与文档模型

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

NoSQL

采用NoSQL数据库有这样几个驱动因素,包括:

  • 比关系数据库更好的扩展性需求,包括支持超大数据集或超高写入吞吐量。
  • NoSQL普遍是免费和开源软件。
  • 关系模型不能很好地支持一些特定的查询操作
  • 关系模型具有一些限制性,NoSQL是更具动态和表达力的数据模型。

对象—关系不匹配

现在大多数应用开发都采用面向对象的编程语言,由于兼容性问题,普遍对SQL数据模型存在抱怨:如果数据存储在关系表中,那么应用层代码中的对象与表、行和列的数据库模型之间需要一个笨拙的转换层。

例如,如何在关系模式中表示简历。整个简历可以通过唯一的标识符user_id来标识。像first_name和last_name这样的字段在每个用户中只出现一次,所以可以将其建模为users表中的列。然而,大多数人在他们的职业中有一个以上的工作,并且可能有多个教育阶段和任意数量的联系信息。用户与这些项目之间存在一对多的关系,可以用多种方式来表示:

  • 在传统的SQL模型(SQL: 1999之前)中,最常见的规范化表示是将职位、教育和联系信息放在单独的表中,并使用外键引用users表,如下图所示。
  • 之后的SQL标准增加了对结构化数据类型和XML数据的支持。这允许将多值数据存储在单行内,并支持在这些文档中查询和索引。Oracle、IBM DB2、MSSQL Server和PostgreSQL都不同程度上支持这些功能。一些数据库也支持JSON数据类型,例如IBM DB2、MySQL和PostgreSQL。
  • 第三个选项是将工作、教育和联系信息编码为JSON或XML文档,将其存储在数据库的文本列中,并由应用程序解释其结构和内容。对于此方法,通常不能使用数据库查询该编码列中的值。

【分布式—基础】数据模型与查询语言_第1张图片

对于像简历这样的数据结构,它主要是一个自包含的文档(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"。为什么这样做呢?

如果用户界面是可以输入地区或行业的自由文本字段,则将其存储为纯文本字符串更有意义。但是,拥有地理区域和行业的标准化列表,并让用户从下拉列表或自动填充器中进行选择会更有优势,这样:

  • 所有的简历保持样式和输入值一致
  • 避免歧义(例如,如果存在一些同名的城市)。
  • 易于更新:名字只保存一次,因此,如果需要改变(例如,由于政治事件而更改城市名称),可以很容易全面更新。
  • 本地化支持:当网站被翻译成其他语言时,标准化的列表可以方便本地化,因此地区和行业可以用查看者的母语来显示。
  • 更好的搜索支持:例如,搜索华盛顿州的慈善家可以匹配到这个简历,这是因为地区列表可以将西雅图属于华盛顿的信息编码进来(而从“大西雅图地区”字符串中并不能看出来西雅图属于华盛顿)。

使用ID的好处是,因为它对人类没有任何直接意义,所以永远不需要直接改变:即使ID标识的信息发生了变化,它也可以保持不变。任何对人类有意义的东西都可能在将来某个时刻发生变更。如果这些信息被复制,那么所有的冗余副本也都需要更新。这会导致更多写人开销,并且存在数据不一致的风险(信息的一些副本被更新,而其他副本未更新)。消除这种重复正是数据库规范化的核心思想。

然而这种数据规范化需要表达多对一的关系(许多人生活在同一地区,许多人在同一行业工作),这并不是很适合文档模型。

文档数据库是某种方式的层次模型:即在其父记录中保存了嵌套记录(一对多关系,如前面例子中的positions、education和contact_info),而不是存储在单独的表中。

但是,在表示多对一和多对多的关系时,关系数据库和文档数据库并没有根本的不同:在这两种情况下,相关项都由唯一的标识符引用,该标识符在关系模型中被称为外键,在文档模型中被称为文档引用。标识符可以查询时通过联结操作或相关后续查询来解析。

对于关系数据库,由于支持联结操作,可以很方便地通过ID来引用其他表中的行。而在文档数据库中,一对多的树状结构不需要联结,支持联结通常也很弱。

即使应用程序的初始版本非常适合采用无联结的文档模型,但随着应用支持越来越多的功能,数据也变得更加互联一体化。

关系数据库与文档数据库现状

在比较关系数据库与文档数据库时,需要考虑很多方面的差异,包括它们的容错性和并发处理,这里只关注数据模型中的差异。

支持文档数据模型的主要论点是模式灵活性,由于局部性而带来较好的性能,对于某些应用来说,它更接近于应用程序所使用的数据结构。关系模型则强在联结操作、多对一和多对多关系更简洁的表达上,与文档模型抗衡。

哪种数据模型的应用代码更简单?

如果应用数据具有类似文档的结构(即一对多关系树,通常一次加载整个树),那么使用文档模型更为合适。而关系型模型则倾向于某种数据分解,它把文档结构分解为多个表,有可能使得模式更为笨重,以及不必要的应用代码复杂化。

文档模型也有一定的局限性:例如,不能直接引用文档中的嵌套项,而需要说“用户251的职位列表中的第二项”(非常类似于层次模型中的访问路径)。然而,只要文档嵌套不太深,这通常不是问题。

在文档数据库中,对联结的支持不足是否是问题取决于应用程序。例如,在使用文档数据库记录事件发生时间的应用分析程序中,可能永远不需要多对多关系。

但是,如果应用程序确实使用了多对多关系,那么文档模型就变得不太吸引人。可以通过反规范化来减少对联结的需求,但是应用程序代码需要做额外的工作来保持非规范化数据的一致性。通过向数据库发出多个请求,可以在应用程序代码中模拟联结,但是这也将应用程序变得复杂,并且通常比数据库内的专用代码执行的联结慢。在这些情况下,使用文档模型会导致应用程序代码更复杂、性能更差。

通常无法一概而论哪种数据模型的应用代码更简单。这主要取决于数据项之间的关系类型。对于高度关联的数据,文档模型不太适合,关系模型可以胜任,而图模型则是最为自然的。

文档模型中的模式灵活性

大多数文档数据库,以及关系数据库中的JSON支持,都不会对文档中的数据强制执行任何模式验证。关系数据库中的XML通常支持带有可选的模式验证功能。没有模式验证意味着可以将任意的键-值添加到文档中,并且在读取时,客户端无法保证文档可能包含哪些字段。

文档数据库有时被称为无模式,但这具有误导性,因为读数据的代码通常采用某种结构因而存在某种隐形模式,而不是由数据库强制执行。更准确的术语应该是读时模式(数据的结构是隐式的,只有在读取时才解释),与写时模式(关系数据库的一种传统方法,模式是显式的,并且数据库确保数据写入时都必须遵循)相对应。

读时模式类似编程语言中的动态(运行时)类型检查,而写时模式类似于静态(编译时)类型检查。正如静态与动态类型检查的支持者对于它们的优缺点存在很大的争议一样,数据库的模式执行也是一个有争议的话题,通常没有明确正确或错误的答案。

如果集合中的项由于某种原因(例如数据异构),并不都具有相同的结构,例如:

  • 有许多不同类型的对象,将每种类型的对象都保存在各自的表中不太现实。
  • 数据的结构由无法控制的外部系统所决定,而且可能随时改变。

在这些情况下,模式带来的损害大于它所能提供的帮助,无模式文档可能是更自然的数据模型。但是,当所有记录都有相同结构时,模式则是记录和确保这种结构的有效机制。

查询的数据局部性

文档通常存储为编码为JSON、XML或其二进制变体(如MongoDB的BSON)的连续字符串。如果应用程序需要频繁访问整个文档,则存储局部性具有性能优势。如果数据被划分在多个表中(关系模型),则需要进行多次索引查找来检索所有数据,中间可能需要更多的磁盘I/O并花费更多的时间。

局部性优势仅适用需要同时访问文档大部分内容的场景。由于数据库通常会加载整个文档,如果应用只是访问其中的一小部分,则对于大型文档数据来讲就有些浪费。对文档进行更新时,通常会重写整个文档,而只有修改量不改变源文档大小时,原地覆盖更新才更有效。因此,通常建议文档应该尽量小且避免写人时增加文档大小。这些性能方面的不利因素大大限制了文档数据库的适用场景。

文档数据库与关系数据库的融合

随着时间的推移,似乎关系数据库与文档数据库变得越来越相近,或许这是一件好事:数据模型可以相互补充。如果数据库能够很好处理文档类数据,还能对其执行关系查询,那么应用程序可以使用最符合其需求的功能的组合。

融合关系模型与文档模型是未来数据库发展的一条很好的途径。

数据查询语言

当关系模型是最初被引入时,就包含了查询数据的新方法:SQL是一种声明式查询语言,而IMS和CODASYL则是命令式。这种差别意味着什么呢?

命令式语言告诉计算机以特定顺序执行某些操作。你完全可以推理整个过程,逐行遍历代码、评估相关条件、更新对应的变量,并决定是否再循环一遍。

而对于声明式查询语言(如SQL或关系代数),则只需指定所需的数据模式,结果需满足什么条件,以及如何转换数据(例如,排序、分组和聚合),而不需指明如何实现这一目标。数据库系统的查询优化器会决定采用哪些索引和联结,以及用何种顺序来执行查询的各个语句。

你可能感兴趣的:(分布式)