NoSql参考数据

19

免责声明:所述的参考数据并不意味着参照完整性

我正在学习Nosql,并希望了解如何建模数据。例如,在CMS应用程序的典型关系数据库中,您可能有两个表:文章和作者,其中文章引用了作者。

在Nosql系统中,您可以以这种方式创建文章文档,因为它们只是伪装的对象图形

{
title: "Learn nosql in 5 minutes",
slug: "nosql_is_easy", 
author: {firstName: "Smarty"
          lastName: "Pants"
}

{
title: "Death to RDBMS",
slug: "rdbms_sucks", 
author: {firstName: "Smarty"
          lastName: "Pants"
}

等等...

假设有一天,聪明的先生决定改名为普通人,因为nosql已经变得无处不在。在这种情况下,每篇文章都需要被扫描并更新作者的姓名。

那么我的问题是,如何在nosql中建模数据以适应CMS的基本用例,从而使性能与RDBMS相当或更快?例如,mongodb 将 CMS 作为一个用例...

编辑:

一些人已经建议将数据规范化,例如:

article 
{
title: "Death to RDBMS",
slug: "rdbms_sucks", 
author: {id: "10000001"}
}

author
{
name: "Big Brother",
id: "10000001"
}

然而,由于nosql的设计缺乏连接,您将不得不使用类似于mapreduce的函数来汇集数据。如果这是您的建议,请评论此类操作的性能。
如果您认为nosql不适合需要引用数据的任何类型数据,请解释原因。这似乎使nosql的用例相当有限,因为任何合理的应用程序都会包含关系数据。
Nosql并不意味着非关系。Nosql doesn't mean non-relational

FYI,术语应为关系型而非理性的。 - Adam Robinson
出于好奇,当你的数据明显是关系型的时候,为什么要使用“NoSQL”数据库? - Adam Robinson
1
因为我想了解何时以及何时不使用NoSQL,超出了明显的用例,如会话存储或数据仓库... - ltfishie
6个回答

6

您的数据显然是关系型的:一篇文章有一个作者。您可以像在关系型存储中一样,在NOSQL存储(如MongoDB)中对数据进行建模,但是因为数据库中没有连接,所以您需要进行两次数据库调用,这样并没有获得任何优势。

但是...您可以使用NOSQL存储来部分去规范化数据以获得更好的性能(一次往返即可获取显示文章所需的所有内容),但是要牺牲即时一致性:以交换始终准确的作者姓名为代价获得最终准确的作者姓名。

例如,您可以在文章中使用以下内容:

author: {firstName: "Smarty", lastName: "Pants", _id:DE342624EF }

现在您可以快速显示文章,并且当有人更改其名称时,您可以启动后台任务来更新所有现有文章,或者可以等待定期的一致性扫描来修复它。
许多主要的网站不再提供即时一致性。您所做的更改仅在某些情况下被其他用户看到。

1
+1 是为了解释最终一致性的权衡。我一直认为这是基于 CAP 定理的低级复制,但异步更新也是可以接受的,没有任何理由不允许它。 - ltfishie
5
为了准确起见:他的数据不是基于“一篇文章有一个作者”的关系而建立的,因此它不是关系型的;当他使用关系(也称为表)来对其进行建模时,它就是关系型的。他可以选择使用单个表格来实现,但仍然是关系型的。 - Marcello Nuccio

4

我觉得如果你这么说的话,CouchDB是一个NoSQL数据库。

但实际上,我们有“通用”编程语言和“特定领域”的语言。同样地,CouchDB是一个“特定领域的数据库”。

我经常使用CouchDB,但我并不在乎它是否使用SQL或NoSQL。对我来说,CouchDB很有价值,因为其API是100%基于HTTP、JSON和Javascript的。您可以构建Web应用程序,浏览器从CouchDB获取HTML,然后通过AJAX查询数据。说它“不是SQL”就是一种低估!

无论如何,回到“聪明人”和“普通人”。也许他有10万个文档。如果我们只是用艰难的方式更新它们呢?那么需要的Javascript代码量非常少。

$.getJSON('/db/_design/cms/_view/by_user?key=Smarty+Pants', {
  success: function(result) {
    // Change the name right here, in the result objects.
    var docs = result.rows.map(function(row) {
      row.value.firstName = "Regular";
      row.value.lastName = "Joe";
      return row.value;
    })

    // Store it!
    $.post('/db/_bulk_docs', {"docs":docs}, function() {
      console.log("Done! Renamed Smarty Pants in " + docs.length + " documents!");
    })
  }
})

是的,这种技术会让在计算机科学课上得到不及格的成绩。但是我喜欢它。我会在我的浏览器中使用Firebug写这段代码。重命名不是原子性的,也没有参照完整性。另一方面,它可能只需要几秒钟就能完成,而且没有人会在意。
你可能会说CouchDB在炒作和基准测试方面失败了,但在吃苦耐劳的学校里却获得了好成绩。
附言:by_user视图是通过MapReduce构建的。在CouchDB中,MapReduce是增量式的,这意味着它的表现类似于大多数SQL索引。所有查询都可以在短时间内(对数时间)完成。

谢谢您的回答。我也关心实际性能。我的例子可能不是最好的,因为一个人多久会改变他的名字呢?但是如果这是您系统中经常发生的操作,那该怎么办呢? - ltfishie
2
首先要注意的是,您正在调用“频繁”操作,这些操作在数据中已经深深地扎根。我不确定这是否在实际应用程序中发生。(Google BigTable和Amazon Dynamo共享CouchDB的设计,它们的CMS运行良好。)但这是一个有效的问题!重命名需要2个HTTP查询,在2秒内完成。即使每天有1,000个用户更改其名称,那也是每1.4分钟进行一次2秒的名称更改。看起来足够了!但更重要的是:CouchDB存在弱点和缺陷。它们可能很严重!但有时它们是值得权衡的。 - JasonSmith

1

针对您的特定情况,使用享元模式,存储对象ID而不是对象实体。

article 
{
title: "Death to RDBMS",
slug: "rdbms_sucks", 
author: {id: "10000001"}
}

author
{
name: "Big Brother",
id: "10000001"
}

如需一般的mongodb架构设计建议,请阅读官方文档


0

请允许我声明,我并不是 NoSQL 方面的专家,我的了解大多基于理论。

话虽如此,我坚信在 NoSQL 中实现类似 CMS 的系统可能不是最佳选择,因为数据主要是关系型的。

对于这个问题,我的看法基于您使用的 NoSQL 系统是否允许通过“主键”类型结构加载记录。我认为大多数都可以,但肯定有一些不能。

话虽如此,我建议按以下方式存储数据。

对于作者:

{
_KEY: $AUTHOR_GUID,
firstName: "Smarty",
lastName: "Pants",
}

至于帖子本身:

{
title: "Learn nosql in 5 minutes",
slug: "nosql_is_easy", 
author: $AUTHOR_GUID,
}

请注意,在上述代码中,我使用_KEY来表示这是“主键”类型的值。
加载帖子后,您可以通过其GUID加载作者。

2
谢谢。这将是规范化(关系型)方法。然而,现在你面临的问题是,由于nosql不支持连接,当你检索文章时,你需要在循环中查找每个作者,或使用mapreduce创建一个视图。 - ltfishie

0

使用playOrm可以很好地对数据进行建模,并在noSQL存储中执行联接操作。playOrm具有可扩展SQL(S-SQL),这是SQL的一种变体,您可以指定要查询的分区。通过这种方式,您可以从DBMS转移到noSQL,仍然拥有您所熟悉的相同工具。


0
这篇文章已经存在一段时间了,但我想指出另一种处理CouchDB中的“连接”和跨文档引用的方法。这是我在CMS中使用的一种方法,我正在重新编写它以使用CouchDB(之前它是为MySQL编写的)。
该CMS名为BlueInk,可以在Github上找到http://github.com/BigBlueHat/BlueInk。目前,重写侧重于文档设计和“渲染引擎”部分,因此没有UI可言——您必须手工制作所有JSON。我希望尽快解决这个问题,但是一旦安装到CouchDB中,存储库中已经有足够的内容可以让您了解如何执行“连接”。
在BlueInk中,页面引用内容项,这些内容项本身可以包含在一个或多个页面中(或同一页多次)。页面通过它们的ID引用页面项(就像您第二个JSON示例中的那样)。当通过{{link1:“page_and_items”视图}}运行时,它将生成输出,可与CouchDB的?include_docs=true查询参数一起使用,以拉取页面文档中引用的内容项的完整内容。
然后,视图输出通过_list函数传递,并通过Mustache模板格式化,并作为HTML页面输出 - 所有这些都在单个GET请求中完成。
在您上面的用例中,可以使用相同的使用引用ID和?include_docs=true的模式。使用_list函数完全是“装饰性”的,但对于重组输出视图JSON或对其进行模板化并输出HTML、CSV、XML等非常有帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接