CouchDB – 简短回顾





4.00/5 (4投票s)
在我们最近的一个项目中,我们与 Apache CouchDB NoSQL 数据库进行了一次为期两年的短暂“冒险”。在这里,我将简要介绍它的优点和缺点。背景 为什么要使用另一个 NoSQL 数据库? CouchDB 是根据项目需求和假设选择的。
在我们最近的一个项目中,我们与 Apache CouchDB NoSQL 数据库进行了一次为期两年的短暂“冒险”。在这里,我将简要介绍它的优点和缺点。
背景
为什么要使用另一个 NoSQL 数据库? CouchDB 是根据项目需求和假设选择的。特别是,易于实现的多主复制在项目的背景下似乎很有吸引力,该项目旨在成为一个分布式文档数据库,没有任何关系且数据相对非结构化。不幸的是,随着我们深入研究项目,这些假设被证明并非 100% 正确,有时使用这项技术会让人感到痛苦。
特点
Apache CouchDB 提供什么?
- RESTful API – 实际上,除了 HTTP 之外没有其他接口。要启动它,您只需要至少有一个可用的 TCP/IP 端口。
- JSON 和 JavaScript – CouchDB 存储和提供 JSON 文档,并使用 JavaScript 在验证或查询期间对其进行处理。值得注意的是,您也可以附加您喜欢的 JavaScript 以外的脚本语言。
- 无模式 – 但是,您仍然可以定义自己的每个文档的验证函数。
- 可伸缩性 – 制造商声称它在单台机器上运行效率很高,并且可以通过复制扩展到多台机器。
- 多主异步复制 – 文档可以双向复制到多个实例,并且每个实例可以同时修改所有文档。
- 由于 CouchDB 复制的异步和多主特性,我们还内置了乐观锁定。CouchDB 在每个文档中维护“_rev”(修订版本)字段。
在 CouchDB 的情况下,NoSQL 意味着我们既没有由数据库维护的事务,也没有关系。数据库也不提供悲观锁定。不幸的是,我们的数据中有一些关系。处理这些是可能的,但相当困难,因为一切都必须手动完成。
数据组织
CouchDB 将数据组织存储在
- 数据库
- 文档
所有以下划线开头的名称都已被系统保留,用于存储其元数据 – 因此实际数据与元数据混合在一起。这使得一切都统一且简单,因为一切都是文档。另一方面,例如,您通常应该从查询结果中过滤掉特殊文档。
文档验证
设计文档是您可以定义文档验证函数的地方
{ "_id": "_design/assets", "language": "javascript", "validate_doc_update": "function(newDoc, oldDoc, userCtx) { if (newDoc.address === undefined) { throw({forbidden: 'Document must have an address.'}); }" } }
您应该注意一件事,即来自复制的文档也会被验证,因此所有复制实例的验证必须相同。当单个文档的验证失败时,复制过程将继续处理下一个文档。实际上,更改会被默默忽略,导致难以查找和修复的不一致。
查询数据
您不能简单地使用“动态”查询 CouchDB。所有可能的查询都应提前定义,这也可以在“设计文档”中完成。此类查询称为“视图”,实际上是 B 树索引。这些索引以 map/reduce 的方式定义,在此处简要讨论:http://docs.couchdb.org/en/latest/intro/tour.html#running-a-query-using-mapreduce
有这样的定义
{ "_id": "_design/assets", "views": { "by_schema_version": { "map": "function(doc) { emit(doc.schemaVersion, 123); }" } } }
我们可以查询这个
{ "total_rows": 53, "offset": 0, "rows": [ { "id": "a", "key": 1, "value": 123 },{ "id": "b", "key": 2, "value": 123 }]}
您应该记住,这些索引在查询时更新。考虑到这一点,以及不同视图与不同参数的快速增长(一切都必须提前定义),有时可能会导致查询期间出现延迟。特别是,如果自上次索引更新以来大量文档已更改。
当您需要动态搜索查询时,可以使用 CouchDB 之上的某个层 – 例如 Lucene 或 ElasticSearch。
复制
CouchDB 复制是我们使用 CouchDB 的主要原因。它可以由“_replicator”特殊数据库驱动,该数据库作为独立进程运行,并在两个实例之间复制数据。您只需要知道这两个 CouchDB 实例的 URL。
冲突解决
复制中的主要问题是冲突解决。由于所有实例都可以修改所有文档,因此有时可能会出现这种情况:两个实例更改了同一个文档,而不知道在另一个位置发生了更改。下图说明了这种情况
最具问题的情况是当一个实例删除一个文档而另一个实例修改该文档时。CouchDB 处理得不好,因为您无法检查已删除文档的内容。此外,在此领域存在一些错误,并且保存此信息的元字段(“_deleted_conflicts”)是未记录的。在删除-更新冲突的情况下,您应该选择“删除优先”策略,或者引入您自己的“已删除”标志以您通常的方式选择获胜者。
维护
CouchDB 不需要用户太多的关注。它相当稳定,并且仅在关键情况下(磁盘空间不足等)崩溃。但是,由于 CouchDB 通常运行许多进程,有时在崩溃后,数据库可能不会完全停止。这可能会令人烦恼,因为您需要手动查找所有孤立的进程并终止它们。使用默认的启动/停止脚本有时会导致两个实例同时运行并记录错误。
当您进行大量修改活动时,索引可能会快速增长,有时占用的磁盘空间甚至超过实际数据。为避免这种情况,您还应考虑安排数据库的“压缩”。
当一切顺利时,您可以通过日志找到所有请求并调查发生了什么。不幸的是,来自批量更新的活动(以及因此来自使用批量请求的复制)没有被清楚地标记,并且跟踪它有时会很困难。此外,来自崩溃和未处理情况的日志对于不熟悉 Erlang 编程语言的人来说没有任何作用。
结论
CouchDB 本身似乎是一个简单易用的文档存储。它提供了一些非常有用的开箱即用功能,例如乐观锁定和轻松复制。由于它只使用 JSON 和 HTTP,可以直接从浏览器 JavaScript 使用。
CouchDB 的一个很好的使用示例是当您不经常更改文档数据并且可以提前定义所有可能的查询时。在某些应用程序中,它也有一些缺点,我试图指出。
您在使用基于文档数据库的生产级系统方面有哪些经验?
外部链接和进一步阅读
[1] https://couchdb.apache.ac.cn/
[2] http://pouchdb.com/ – PouchDB,浏览器中简单的文档数据库,可以与 CouchDB 实例同步。
它似乎只是一个小的工具,但也许有人会觉得它有用。
[3] https://hibernate.com.cn/ogm/ – Hibernate OGM 与 CouchDB 的集成。不幸的是,当我们开始我们的项目时,没有成熟可靠的 CouchDB 客户端,所以我们不得不自己编写。OGM 将完全支持 CouchDB,所以它可能会非常有用。