1.1. 技术概述¶
1.1.1. 文档存储¶
CouchDB 服务器托管命名数据库,这些数据库存储 **文档**。每个文档在数据库中都有唯一的名称,CouchDB 提供了一个 RESTful HTTP API 用于读取和更新(添加、编辑、删除)数据库文档。
文档是 CouchDB 中主要的数据单位,包含任意数量的字段和附件。文档还包含由数据库系统维护的元数据。文档字段具有唯一的名称,包含 不同类型 的值(文本、数字、布尔值、列表等),并且文本大小或元素数量没有设置限制。
CouchDB 文档更新模型是无锁且乐观的。文档编辑由客户端应用程序加载文档、应用更改并将它们保存回数据库来完成。如果另一个编辑相同文档的客户端首先保存了他们的更改,则客户端会在保存时收到编辑冲突错误。要解决更新冲突,可以打开最新的文档版本,重新应用编辑并再次尝试更新。
单个文档更新(添加、编辑、删除)都是全部或无,要么完全成功,要么完全失败。数据库永远不会包含部分保存或编辑的文档。
1.1.2. ACID 属性¶
CouchDB 文件布局和提交系统具有所有 原子一致隔离持久 (ACID) 属性。在磁盘上,CouchDB 永远不会覆盖已提交的数据或相关结构,确保数据库文件始终处于一致状态。这是一种“仅崩溃”设计,其中 CouchDB 服务器不会经过关闭过程,它只是被终止。
文档更新(添加、编辑、删除)是串行的,除了二进制 Blob 可以并发写入。数据库读取器永远不会被锁定,也不必等待写入器或其他读取器。任意数量的客户端可以读取文档,而不会被锁定或被并发更新中断,即使是在同一文档上也是如此。CouchDB 读取操作使用 多版本并发控制 (MVCC) 模型,其中每个客户端都看到从读取操作开始到结束的数据库一致快照。这意味着 CouchDB 可以保证每个文档的交易语义。
文档在 B 树 中按其名称(DocID)和序列 ID 索引。对数据库实例的每次更新都会生成一个新的顺序号。序列 ID 稍后用于增量查找数据库中的更改。这些 B 树索引在保存或删除文档时会同时更新。索引更新始终发生在文件末尾(追加更新)。
文档的优势在于数据已经方便地打包以供存储,而不是像大多数数据库系统那样分散在多个表和行中。当文档提交到磁盘时,文档字段和元数据被打包到缓冲区中,一个接一个地顺序排列(这在稍后构建视图时非常有用)。
当 CouchDB 文档被更新时,所有数据和相关索引都会被刷新到磁盘,并且事务提交始终使数据库处于完全一致的状态。提交发生在两个步骤中
所有文档数据和相关索引更新都会同步刷新到磁盘。
更新的数据库头以两个连续的相同块写入,构成文件的第一个 4k,然后同步刷新到磁盘。
如果在步骤 1 期间发生操作系统崩溃或电源故障,则部分刷新的更新在重新启动时会被简单地忽略。如果在步骤 2(提交头)期间发生此类崩溃,则先前相同头的生存副本将保留,确保所有先前提交数据的连贯性。除了头区域之外,崩溃或电源故障后不需要一致性检查或修复。
1.1.3. 压缩¶
偶尔压缩会回收浪费的空间。按计划或当数据库文件超过一定量的浪费空间时,压缩过程会将所有活动数据克隆到一个新文件中,然后丢弃旧文件。数据库在整个过程中始终保持完全在线状态,所有更新和读取都允许成功完成。只有当所有数据都被复制并且所有用户都迁移到新文件时,才会删除旧的数据库文件。
1.1.4. 视图¶
ACID 属性只处理存储和更新,但我们还需要能够以有趣且有用的方式显示我们的数据。与必须将数据仔细分解成表的 SQL 数据库不同,CouchDB 中的数据存储在半结构化文档中。CouchDB 文档很灵活,每个文档都有自己的隐式结构,这减轻了双向复制表模式及其包含数据的最困难问题和陷阱。
但是,除了充当一个花哨的文件服务器之外,用于数据存储和共享的简单文档模型过于简单,无法构建真实的应用程序——它 simply doesn’t do enough of the things we want and expect. 我们希望切片和切块,并以多种不同的方式查看我们的数据。我们需要一种方法来过滤、组织和报告尚未分解成表的 data。
另请参阅
1.1.4.1. 视图模型¶
为了解决将结构添加回非结构化和半结构化数据的这个问题,CouchDB 集成了一个视图模型。视图是聚合和报告数据库中文档的方法,它们是按需构建的,用于聚合、联接和报告数据库文档。由于视图是动态构建的并且不会影响底层文档,因此您可以根据相同数据创建任意数量的不同视图表示。
视图定义严格来说是虚拟的,只显示当前数据库实例中的文档,这使得它们与它们显示的数据分离,并与复制兼容。CouchDB 视图在特殊的 **设计文档** 中定义,并且可以像普通文档一样跨数据库实例复制,因此不仅数据在 CouchDB 中复制,整个应用程序设计也会复制。
1.1.4.2. JavaScript 视图函数¶
视图使用充当 map-reduce 系统 中映射部分的 JavaScript 函数定义。一个 视图函数 以 CouchDB 文档作为参数,然后执行它需要执行的任何计算来确定要通过视图提供的数据(如果有)。它可以根据单个文档向视图添加多个行,也可以根本不添加任何行。
另请参阅
1.1.4.3. 视图索引¶
视图是数据库实际文档内容的动态表示,CouchDB 使创建有用的数据视图变得容易。但是,生成包含数十万或数百万个文档的数据库视图需要时间和资源,这不是系统应该每次都从头开始做的事情。
为了保持视图查询速度,视图引擎维护其视图的索引,并增量更新它们以反映数据库中的更改。CouchDB 的核心设计在很大程度上围绕着高效、增量创建视图及其索引的需要进行了优化。
视图及其函数在特殊的“设计”文档中定义,一个设计文档可以包含任意数量的唯一命名的视图函数。当用户打开视图并自动更新其索引时,同一设计文档中的所有视图都作为一个组进行索引。
视图构建器使用数据库序列 ID 来确定视图组是否与数据库完全同步。如果不是,则视图引擎检查自上次刷新以来更改的所有数据库文档(按打包顺序排列)。文档按它们在磁盘文件中出现的顺序读取,减少了磁盘磁头寻址的频率和成本。
视图可以同时读取和查询,同时也可以刷新。如果一个客户端正在缓慢地流出大型视图的内容,则可以为另一个客户端同时打开和刷新同一个视图,而不会阻塞第一个客户端。对于任意数量的并发客户端读取器,这都是成立的,它们可以在索引并发刷新给其他客户端时读取和查询视图,而不会给读取器造成问题。
当文档通过您的“map”和“reduce”函数由视图引擎处理时,如果存在,它们的先前行值将从视图索引中删除。如果文档被视图函数选中,则函数结果将作为新行插入视图。
当视图索引更改写入磁盘时,更新始终追加到文件末尾,这既可以减少磁盘提交期间的磁盘磁头寻道时间,也可以确保崩溃和电源故障不会导致索引损坏。如果在更新视图索引时发生崩溃,则不完整的索引更新将被简单地丢失,并从其先前提交的状态增量重建。
1.1.5. 安全性和验证¶
为了保护谁可以读取和更新文档,CouchDB 具有一个简单的读取器访问和更新验证模型,可以扩展以实现自定义安全模型。
另请参阅
1.1.5.1. 管理员访问¶
CouchDB 数据库实例具有管理员帐户。管理员帐户可以创建其他管理员帐户并更新设计文档。设计文档是包含视图定义和其他特殊公式的特殊文档,以及常规字段和 Blob。
1.1.5.2. 更新验证¶
当文档写入磁盘时,它们可以通过 JavaScript 函数动态验证,以进行安全性和数据验证。当文档通过所有公式验证标准时,允许更新继续。如果验证失败,则更新将中止,用户客户端将收到错误响应。
用户的凭据和更新后的文档都作为输入提供给验证公式,并且可用于通过验证用户更新文档的权限来实现自定义安全模型。
一个基本的“仅作者”更新文档模型很容易实现,其中文档更新被验证以检查用户是否在现有文档的“作者”字段中列出。更动态的模型也是可能的,例如检查单独的用户帐户配置文件以获取权限设置。
更新验证对实时使用和复制更新都强制执行,确保共享分布式系统中的安全性和数据验证。
另请参阅
1.1.6. 分布式更新和复制¶
CouchDB 是一个基于对等的分布式数据库系统。它允许用户和服务器在断开连接的情况下访问和更新相同共享数据。然后,这些更改可以在以后双向复制。
CouchDB 文档存储、视图和安全模型旨在协同工作,使真正的双向复制高效且可靠。文档和设计都可以复制,允许完整的数据库应用程序(包括应用程序设计、逻辑和数据)复制到笔记本电脑以供离线使用,或者复制到远程办公室的服务器,在这些办公室中,缓慢或不可靠的连接使得共享数据变得困难。
复制过程是增量的。在数据库级别,复制仅检查自上次复制以来更新的文档。如果复制在任何步骤失败,例如由于网络问题或崩溃,则下次复制将从最后一个检查点重新开始。
可以创建和维护部分副本。复制可以通过 JavaScript 函数过滤,以便仅复制特定文档或满足特定条件的文档。这允许用户将大型共享数据库应用程序的子集离线用于自己的用途,同时保持与应用程序和该数据子集的正常交互。
1.1.6.1. 冲突¶
冲突检测和管理是任何分布式编辑系统的关键问题。CouchDB 存储系统将编辑冲突视为一种常见状态,而不是异常状态。冲突处理模型简单且“非破坏性”,同时保留单个文档语义并允许分散的冲突解决。
CouchDB 允许在数据库中同时存在任意数量的冲突文档,每个数据库实例确定性地决定哪个文档是“获胜者”,哪些是冲突。只有获胜文档才能出现在视图中,而“失败”的冲突仍然可以访问,并保留在数据库中,直到在数据库压缩期间被删除或清除。由于冲突文档仍然是常规文档,因此它们像常规文档一样复制,并受相同的安全性和验证规则约束。
当发生分布式编辑冲突时,每个数据库副本都会看到相同的获胜修订版,并且每个副本都有机会解决冲突。解决冲突可以手动完成,也可以根据数据的性质和冲突,由自动化代理完成。该系统使分散的冲突解决成为可能,同时保持单个文档数据库语义。
即使多个断开连接的用户或代理尝试解决相同的冲突,冲突管理也能继续工作。如果解决的冲突导致更多冲突,则系统以相同的方式容纳它们,在每台机器上确定相同的获胜者,并保持单个文档语义。
另请参阅
1.1.6.2. 应用程序¶
仅使用基本复制模型,许多传统上是单服务器数据库的应用程序几乎无需额外工作即可实现分布式。CouchDB 复制旨在立即对基本数据库应用程序有用,同时也可以扩展以用于更复杂和功能齐全的用途。
只需很少的数据库工作,就可以构建一个具有细粒度安全性和完整修订历史记录的分布式文档管理应用程序。可以实现对文档的更新以利用增量字段和 Blob 复制,其中复制的更新几乎与实际编辑差异(“diff”)一样高效和增量。
1.1.7. 实现¶
CouchDB 基于 Erlang OTP 平台,这是一种函数式并发编程语言和开发平台。Erlang 是为实时电信应用程序开发的,非常重视可靠性和可用性。
在语法和语义方面,Erlang 与 C 或 Java 等传统编程语言非常不同。Erlang 使用轻量级“进程”和消息传递来实现并发,它没有共享状态线程,所有数据都是不可变的。Erlang 的强大并发性非常适合数据库服务器。
CouchDB 旨在实现无锁并发,在概念模型和实际的 Erlang 实现中都是如此。减少瓶颈并避免锁可以使整个系统在繁重负载下保持可预测地工作。CouchDB 可以容纳许多客户端复制更改、打开和更新文档以及查询视图,这些视图的索引同时正在为其他客户端刷新,而无需锁。
为了实现更高的可用性和更多并发用户,CouchDB 旨在实现“无共享”集群。在“无共享”集群中,每台机器都是独立的,并与其集群伙伴复制数据,允许单个服务器故障,而不会出现停机时间。而且,由于在重新启动时不需要一致性扫描和修复,如果整个集群发生故障(例如,由于数据中心的电源故障),则整个 CouchDB 分布式系统将在重新启动后立即变得可用。
CouchDB 从一开始就秉持着分布式文档数据库系统的理念。与在相同遗留模型和数据库之上添加分布式功能的笨拙尝试不同,它是经过精心设计、工程和集成的结果。文档、视图、安全和复制模型、专用查询语言、高效且强大的磁盘布局以及 Erlang 平台的并发性和可靠性都经过精心集成,以构建可靠高效的系统。