探索 CouchDB —— 针对 Web 应用程序的面向文档数据库

来源:互联网 发布:假面骑士kabuto知乎 编辑:程序博客网 时间:2024/06/05 01:18

Joe Lennon, 软件开发人员, Core International

2009 年 4 月 27 日

关系数据库定义的结构非常严格,并且也通过严格的方式维护软件应用程序的数据。Apache 的开源 CouchDB 为储存数据提供一种新方法,即使用不需要模式的面向文档的数据库模型。与关系模型高度结构化的数据储存不同,CouchDB 使用基于 JavaScript 的视图模型生成结构化聚合,以半结构化的方式储存数据,并从这些半结构化文档报告结果。CouchDB 一开始就是以 Web 应用程序为主要目标而开发的,并且希望发展成为用于开发 Web 应用程序的标准数据库。

什么是 CouchDB?

CouchDB 是一个开源的面向文档的数据库管理系统,可以通过 RESTful JavaScript Object Notation (JSON) API 访问。术语 “Couch” 是 “Cluster Of Unreliable Commodity Hardware” 的首字母缩写,它反映了 CouchDB 的目标具有高度可伸缩性,提供了高可用性和高可靠性,即使运行在容易出现故障的硬件上也是如此。CouchDB 最初是用 C++ 编写的,但在 2008 年 4 月,这个项目转移到 Erlang OTP 平台进行容错测试。

CouchDB 可以安装在大部分 POSIX 系统上,包括 Linux® 和 Mac OS X。尽管目前还不正式支持 Windows®,但现在已经着手编写 Windows 平台的非官方二进制安装程序。CouchDB 可以从源文件安装,也可以使用包管理器安装(比如在 Mac OS X 上使用 MacPorts)。

CouchDB 是一个顶级 Apache Software Foundation 开源项目,根据 Apache 许可 V2.0 发布。这个开源许可允许在其他软件中使用这些源代码,并根据需要进行修改,但前提是遵从版权需知和免责声明。与许多其他开源许可一样,这个许可允许用户根据需求使用、修改和分发该软件。不一定由同一个许可包含所有修改,因为我们仅维护一个 Apache 代码使用许可需知。

 

面向文档的数据库和关系数据库之间的区别

对很多人而言,刚接触面向文档数据库管理系统这个概念时很难理解它,尤其是长期与关系数据库打交道的人员。这是因为这两个模型相似的地方很少。

顾名思义,面向文档数据库是由一系列自包含的文档组成的。这意味着相关文档的所有数据都储存在该文档中 — 而不是关系数据库的关系表中。事实上,面向文档的数据库中根本不存在表、行、列或关系。这意味着它们是与模式无关的;不需要在实际使用数据库之前定义严格的模式。如果某个文档需要添加一个新字段,它仅需包含该字段,从而不影响到数据库中的其他文档。因此,文档不必为没有值的字段储存空数据值。

即将推出的书 CouchDB: The Definitive Guide(见 参考资料)使用名片作为 “现实的文档”,并介绍了与关系数据库相比,如何在面向文档的数据库中描述它。在关系数据库中,您需要使用 4 个以上的表来储存这些数据:一个 “Person” 表、一个 “Company” 表、一个 “Contact Details” 表和一个用于储存名片本身的表。这些表都有严格定义的列和键,并且使用一系列的连接(join)组装数据。

虽然这样做的优势是每段数据都有一个惟一真实的版本,但这为以后的修改带来不便。此外,也不能修改其中的记录以用于不同的环境。例如,一个人可能有传真号码,而另一个人没有。在名片上不应该显示 “传真:没有”,而是忽略任何关于传真的细节。

在面向文档的数据库中,每个名片都储存在各自的文档中,并且每个文档都可以定义它需要使用的字段。因此,对于没有传真号码的人而言,就不需要定义传真的值,而对于有传真号码的人,则根据他们的意愿定义该值。

这两种数据库的另一个不同点是惟一标识符的储存。在关系数据库中通常可以使用主键,它由一个自动递增特性或序列生成器生成。当然,这些标识符仅相对于所使用的表或数据库是惟一的 — 其他表或数据库还可以使用它们。如果同时对不同网络上的两个数据库执行更新操作,这两个数据库不会同时准确地获取下一个惟一标识符。CouchDB 没有自动递增或序列特性。相反,它为每个文档分配一个通用惟一标识符(Universally Unique Identifier,UUID),这就杜绝了其他数据库意外地选择相同的惟一标识符的情况。

面向文档数据库和关系数据库的另一个重要区别就是面向文档数据库不支持连接。因此 CouchDB 中没有主键和外键,没有基于连接的键。这并不意味着不能从 CouchDB 数据库获取一组关系数据。一个称为视图的特性允许您为没有在数据库中定义的文档创建一种任意关系。这意味着您能够获得典型的 SQL 联合查询的所有好处,但又不需要在数据库层预定义它们的关系。

一定要注意,虽然面向文档数据库的操作方式不同于关系数据库,但这并不意味着它们是可以替换的。CouchDB 的目的并不是替换关系数据库,而是为那些更适合使用面向文档模型(而不是传统的关系数据模型)的项目提供一种选择,比如 wikis、博客和文档管理系统。

 

CouchDB 是如何工作的?

CouchDB 构建在强大的 B-树储存引擎之上。这种引擎负责对 CouchDB 中的数据进行排序,并提供一种能够在对数均摊时间内执行搜索、插入和删除操作的机制。CouchDB 将这个引擎用于所有内部数据、文档和视图。

因为 CouchDB 数据库的结构独立于模式,所以它依赖于使用视图创建文档之间的任意关系,以及提供聚合和报告特性。使用 Map/Reduce 计算这些视图的结果,Map/Reduce 是一种使用分布式计算来处理和生成大型数据集的模型。Map/Reduce 模型由 Google 引入,可分为 Map 和 Reduce 两个步骤。在 Map 步骤中,由主节点接收文档并将问题划分为多个子问题。然后将这些子问题发布给工作节点,由它处理后再将结果返回给主节点。在 Reduce 步骤,主节点接收来自工作节点的结果并合并它们,以获得能够解决最初问题的总体结果和答案。

CouchDB 中的 Map/Reduce 特性生成键/值对,CouchDB 将它们插入到 B-树引擎中并根据它们的键进行排序。这就能通过键进行高效查找,并且提高 B-树中的操作的性能。此外,这还意味着可以在多个节点上对数据进行分区,而不需要单独查询每个节点。

传统的关系数据库管理系统有时使用锁来管理并发性,从而防止其他客户机访问某个客户机正在更新的数据。这就防止多个客户机同时更改相同的数据,但对于多个客户机同时使用一个系统的情况,数据库在确定哪个客户机应该接收锁并维护锁队列的次序时会遇到困难,这很常见。在 CouchDB 中没有锁机制,它使用的是多版本并发性控制(Multiversion concurrency controlMVCC)— 它向每个客户机提供数据库的最新版本的快照。这意味着在提交事务之前,其他用户不能看到更改。许多现代数据库开始从锁机制前移到 MVCC,包括 Oracle(V7 之后)和 Microsoft® SQL Server 2005 及更新版本。

 

文档:CouchDB 数据库的构建块

CouchDB 数据库存储惟一的命名文档并提供一个 RESTful JSON API,该 API 允许应用程序读取和修改这些文档。CouchDB 数据库中的所有数据都储存在一个文档中,并且每个文档可以由未定义的字段组成。这意味着每个文档都可以具有未在其他文档中定义的字段。换句话说,这些文档不受严格的模式的限制。

每个文档还可以包含元数据(关于数据的数据),比如惟一的文档 ID 和修改号。文档字段可以包含各种类型的数据,比如文本字符串、数字和布尔值(true/false)等。字段的大小不受限制。每个字段必须有一个惟一的名称(文档不能包含两个同名的字段)。

当更改 CouchDB 文档时,这些更改实际上并不是附加在原来的文档之上,而是创建整个文档的一个新版本,即 修订。这意味着数据库会自动维护文档修改的完整历史。文档-修订系统管理修订控制,但不包括在数据库中自动完成的修订。

CouchDB 没有锁机制;两个客户机可以同时加载和编辑同一个文档。不过,如果一个客户机保存了所有更改时,另一个客户机在尝试保存更改时将收到一个编辑冲突。可以通过加载更新版本的文档来解决该冲突,然后重新进行编辑并再次保存。CouchDB 通过确保文档全部更新成功或全部失败来保持数据的一致性 — 文档更新要么成功,要么失败。数据库中不会存在仅保存了一部分的文档。

 

视图:从 CouchDB 获取有用信息

CouchDB 的本质是非结构化的,虽然由于缺乏严格的模式而使其获得了更大的灵活性和可伸缩性,但是这使得 CouchDB 在现实应用程序中难以使用。想一下关系数据库,对于每个应用程序,严格定义的表之间的关系对于为数据赋予意义至关重要。不过,当要求实现更高的性能时,则需要创建物化视图来反规范化(de-normalize)数据。在很多情况下,面向文档数据库采用相反的方法处理事情。它将数据储存在一个平面地址空间中,这就像一个完全反规范化的数据仓库。然后它提供一个视图模型为数据添加结构,因此能够聚合数据得出有用的含义。

在 CouchDB 中可以根据需求创建视图,并使用它在数据库中聚合、连接和报告文档。视图是动态创建的,对数据库中的文档没有影响。视图是在设计文档中定义的,并且可以跨实例复制。这些设计文档包含使用 MapReduce 运行查询的 JavaScript 函数。视图的 map 函数将文档作为一个参数,并执行一系列的计算来决定应该对哪些数据使用视图。如果一个视图具有 reduce 函数,就使用它来聚合结果。视图接收一组键和值,然后将它们合并成一个单一的值。

清单 1 是 CouchDB 视图的 map 和 reduce 函数的一个例子,它计算带有附件的文档的数目。

清单 1. 典型的 CouchDB 视图

map: function(doc) {  if (doc._attachments) {    emit("with attachment", 1);  }  else {    emit("without attachment", 1);   }}reduce: function(keys, values) {   return sum(values);}

 

CouchDB 视图可以是储存在设计文档内部的永久性视图,或者是根据需求执行的临时视图。临时视图耗费的资源比较多,并且随着数据库中存储的数据的增长而变慢。因此,CouchDB 视图大部分情况下应该创建在设计文档中。

 

RESTful JSON API

CouchDB 通过提供一个 API 来从数据库中获取数据。这个 API 可以通过 HTTP GETPOST 请求访问,并且使用 JSON 以 JavaScript 对象的形式返回数据。这使执行数据库操作非常容易,并且与应用程序用什么语言开发无关。您可以将 JavaScript 框架和 Ajax 请求对象一起使用,比如 Prototype、JQuery 或 ExtJS — 没有必要在您的 Web 应用程序中使用服务器端语言。

为了保持简洁并且展示由 API 发出的原始 JSON 响应,我们在本文中使用命令行工具 curl。这允许您发出 GETPOSTPUTDELETE 请求,并演示从 Web 服务器(在这个例子中是安装在本地的 CouchDB 服务器)接收到的原始 HTTP 响应。

请求 curl http://127.0.0.1:5984/ 返回以下响应:{"couchdb":"Welcome","version":"0.8.1-incubating"}。这个简单的 API 调用是一个简单的 GET 请求, 其响应通知我们所安装的 CouchDB 的版本。为了显式地定义所发出的请求的类型,我们将使用 curl 的 -X 参数,就像在以下请求中一样:curl -X GET http://127.0.0.1:5984/_all_dbs。这将返回以下结果:[]

在这个例子中,我们请求了一个特别的 CouchDB 视图的 URI,该视图返回包含 CouchDB 服务器上的所有数据库的一个列表。如果我们实际创建了数据库,就会返回一个数据库名数组。但是在这个例子中,仅返回一个空 JavaScript 数组。现在我们创建一些数据库,下一次运行该请求的时候,将看到不同的结果:curl -X PUT http://127.0.0.1:5984/fruit

收到的响应是:{"ok":true}。然后我们发出第二个请求:curl -X PUT http://127.0.0.1:5984/vegetables。收到的响应和前面的一样。现在,我们将再次请求一个数据库列表:curl -X GET http://127.0.0.1:5984/_all_dbs。这次,我们获得比以前更好的结果:["fruit","vegetables"]

当我们创建了这些数据库之后,返回属性 “ok” 并带有布尔值 true。这表明操作是成功的。但是,如果出现异常,应该怎么办?为了让 CouchDB 服务器出现问题,我们创建一个与现有数据库同名的数据库:curl -X PUT http://127.0.0.1:5984/fruit.

这次,我们得到的响应如下所示:{"error":"database_already_exists","reason":"Database /"fruit/" already exists."}

如您所见,CouchDB 尝试创建该数据库,但在这个过程中遇到一个错误。最后,它返回属性 “error” 和错误代码的值(在本例中为 “database_already_exists”)。当然,在现实的应用程序中,我们将检查从 CouchDB 服务器返回的所有响应,看看是否存在错误属性,并根据所发现的错误代码显示一条友好的错误消息。

假如我们不再需要 vegetables 数据库并希望删除它。要在 CouchDB 删除数据库,仅需发出一个 DELETE HTTP 请求,并将该数据库的名称附加到基 URI 中,如下所示:curl -X DELETE http://127.0.0.1:5984/vegetables。这将得到一个成功的响应,其结果和前面执行的 PUT 请求一样。现在我们使用 curl -X GET http://127.0.0.1:5984/_all_dbs 获取一个数据库列表。这将得到以下响应:["fruit"]

如果数据库中没有任何数据,那会出现什么情况呢?清单 2 中的请求创建一个称为 “apple” 的文档。

 

清单 2. 创建一个称为 apple 的文档

curl -X PUT http://127.0.0.1:5984/fruit/apple /-H "Content-Type: application/json" -d {}

 

服务器会做出如下响应:{"ok":true,"id":"apple","rev":"1801185866"}

现在已经创建了一个文档,我们尝试从数据库获取它:curl -X GET http://127.0.0.1:5984/fruit/apple。CouchDB 将做出如下响应:{"_id":"apple","_rev":"1801185866"}

我们发出的最后一个 API 调用将获取关于特定数据库的信息 — 在这个例子中,为 fruit 数据库:curl -X GET http://127.0.0.1:5984/fruit。来自服务器的响应告诉我们关于该数据库的一些有趣信息:

 

清单 3. 来自服务器的响应

{"db_name":"fruit","doc_count":1,"doc_del_count":0,"update_seq":1,"compact_running":false,"disk_size":14263}

 

在这个小节中,我们探索了 CouchDB 通过 RESTful JSON API 接口提供的几个不同 API 方法。在现实中,我们不会亲自编写这些 HTTP 请求,而是由我们选择的编程语言或脚本语言完成它们。

CouchDB 还包含一个称为 Futon 的 Web 应用程序,可将它用作一个 CouchDB 管理工具,它使您能够轻松维护数据库、文档和文档修改。如果您已经将 CouchDB 安装到本地机器的默认端口 5984,则可以通过将浏览器指向以下地址来访问 Futon:http://127.0.0.1:5984/_utils/。图 1 显示了实际使用中的 Futon。

 

图 1. 实际使用中的 CouchDB Futon 实用工具
实际使用中的 CouchDB Futon 实用工具

CouchDB 提供的 RESTful API 可能会让以前使用关系数据库管理系统的人员望而生畏,但它为与数据库进行交互提供了一种独特的高度可访问的方法。传统的数据库系统通常要求使用某些 SQL 客户机建立一个数据库连接,然后该连接接收一系列 SQL 语句,以获取数据和执行 Create、Update、Delete (CRUD) 操作。借助 RESTful JSON API,用户可以使用任何支持 HTTP 协议的软件连接到 CouchDB。事实上,许多现代的编程语言和脚本语言都以某种形式提供 HTTP 协议的接口,这意味着 CouchDB 几乎可以用于任何开发项目。

 

结束语

Apache CouchDB 项目还处于早期发展阶段。CouchDB 是一款正在测试中的软件。CouchDB 在 Web 应用程序、iPhone 应用程序和 Facebook 应用程序中越来越流行。到目前为止,强大的 wiki、博客、讨论论坛和文档管理软件都致力于改善关系数据库,让它们能够更高效地储存文档形式的数据。然而,随着 CouchDB 的发行版越来越稳定,CouchDB 数据库越来越受到这些类型的软件的青睐,从而避免了文档修订管理和不断变化的模式需求带来的烦恼。

总体而言,到目前为止用户对 CouchDB 的反馈都是正面的,尽管很多人觉得有必要在博客和论坛上讨论哪种数据库更好 — 关系型或面向文档型。不过,CouchDB 从来没有打算取代关系数据库,也不期望成为数据库开发的新标准。当然,在很多场景中,CouchDB 的简单性使其不能与 DB2 和 Oracle 相媲美。不过在很多其他场景中,数据库的简单性确实是必要的,传统的 RDBMS 产品被过度吹捧了,并且耗费的资源太多。

 

参考资料

学习

  • CouchDB 简介:从项目的 Web 站点阅读关于 CouchDB 的介绍。
  • 阅读 Technical Overview of CouchDB,这篇文章是关于 CouchDB 的关键模型和组件的概述。
  • 查看 CouchDB Wiki,通过这个详尽的 wiki 找到如何在您的计算机上安装和使用 CouchDB。
  • CouchDB: The Definitive Guide:阅读 O'Reilly Media 的图书,这是关于 Apache CouchDB 的在线免费版本。将在 2009 年 9 月出版。
  • 访问 Planet CouchDB,它综合了关于 CouchDB 的博客。
  • 阅读关于 CouchDB 的 Wikipedia 条目。
  • Twitter 上学习 CouchDB。
  • 要收听针对软件开发人员的有趣访谈和讨论,请访问 developerWorks podcasts
  • 随时关注 developerWorks 技术活动网络广播
  • 了解 developerWorks on Twitter
  • 查阅最近将在全球举办的面向 IBM 开放源码开发人员的研讨会、交易展览、网络广播和其他 活动
  • 访问 developerWorks 开放源码专区,获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。
  • 查看免费的 developerWorks 演示中心,观看并了解 IBM 及开源技术和产品功能。

 

获得产品和技术

  • 使用 IBM 试用软件 改进您的下一个开发项目。
  • 下载 IBM 产品评估版在 IBM SOA Sandbox 中探索在线试用版本,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。


讨论

  • 参与 developerWorks blogs 并加入 developerWorks 社区。

关于作者

Joe Lennon 是一位来自爱尔兰科克市的软件开发人员,他今年 23 岁。Joe 目前为 Core International 工作,是 Web 应用程序和 Oracle PL/SQL 开发人员。他 2007 年毕业于 University College Cork,并获得商业信息系统专业的学位。他现在和他的女朋友 Jill 生活在科克市。

原创粉丝点击