你如何在 NoSQL 中跟踪记录关系?

Posted

技术标签:

【中文标题】你如何在 NoSQL 中跟踪记录关系?【英文标题】:How do you track record relations in NoSQL? 【发布时间】:2011-05-06 19:45:44 【问题描述】:

我试图找出 NoSQL KVP 或文档数据库中外键和索引的等价物。由于没有关键表(用于添加标记两个对象之间关系的键),我真的很难理解如何以对普通网页有用的方式检索数据。

假设我有一个用户,这个用户在网站上留下了很多 cmets。我能想到的跟踪用户 cmets 的唯一方法是

    将它们嵌入到用户对象中(这似乎没什么用) 创建并维护一个 user_id:comments 值,其中包含每个评论的键 [comment:34、comment:197 等...] 的列表,以便我可以根据需要获取它们。

但是,以第二个示例为例,当您使用它来跟踪其他事物(例如称为“active_cmets”的键,其中可能包含 3000 万个 id)时,您很快就会碰壁,这使得它花费一吨 查询每个页面只是为了了解一些最近的活动 cmets。它也很容易出现竞争条件,因为许多页面可能会同时尝试更新它。

如何在 NoSQL 数据库中跟踪如下关系?

用户的所有 cmets 所有活动的 cmets 所有带有 [keyword] 标记的帖子 俱乐部中的所有学生 - 或学生所在的所有俱乐部

还是我想错了?

【问题讨论】:

NoSQL 数据库中没有一种方法可以做到这一点,这个问题类似于询问如何在 C 程序中跟踪关系。 哇,那么我想关于 NoSQL 取代 RDBMS 的炒作是不可能的。 是的,NoSQL 肯定被夸大了。我并不是说新技术在适当的情况下没有用,但认为它们将取代 RDBMS 是荒谬的。见en.wikipedia.org/wiki/Hype_cycle 你不会只有一个“用户”集合和一个 cmets 集合。然后,每个评论都只是一个“作者”属性,其值是对用户 ID 的引用? 【参考方案1】:

    user:userid:cmets 是一种合理的方法 - 可以将其视为 SQL 中的列索引,并附加要求您不能对未索引的列进行查询。

    这是您需要考虑需求的地方。包含 3000 万个项目的列表并非不合理,因为它很慢,而是因为用它做任何事情都不切实际。如果您的真正要求是显示一些最近的 cmets,那么您最好保留一个非常短的列表,只要添加评论就会更新 - 请记住,NoSQL 没有规范化要求。竞争条件是基本键值存储中的列表的问题,但通常要么您的平台正确支持列表,您可以使用锁做一些事情,或者您实际上并不关心失败的更新。

    与用户 cmets 相同 - 创建索引关键字:posts

    更多相同 - 可能是作为学生财产的俱乐部列表和该字段上的索引以获取俱乐部的所有成员

【讨论】:

那么,基本上所有东西都只需要列表?似乎应该有一种比手动跟踪 id 字符串更复杂的方法。一方面,在它们变得有用之前,你只能走这么远。再说一次,NoSQL 技术的主要子项目(MongoDB、CouchDB、Membase 等)都是新项目,所以也许我只需要给他们更多时间来想出更好的方法来跟踪关系。 如果您使用的是 NoSQL(AKA 非关系数据存储),则需要停止以关系术语进行思考。使用的方法在平台之间会有所不同,但是您必须管理索引的基本思想是相当普遍的。您给出的关系示例在 NoSQL 中以两种不同的方式建模:1)存储 - 与 SQL 不同,列可以具有多个/复杂的值,因此子对象只是父对象的一部分。 2) 搜索 - 您的长列表实际上是可搜索性的要求,这意味着索引 - 您可以使用简单的自定义列表或更完整的搜索引擎。【参考方案2】:

couchDB 方法建议在 map 阶段发出适当的东西类别,并在 reduce 中对其进行总结。因此,您可以映射所有 cmets 并为给定用户发出 1,然后只打印出那些。然而,它需要大量的磁盘存储来构建 couchDB 中所有可跟踪数据的持久视图。顺便说一句,他们也有这个关于关系的维基页面:http://wiki.apache.org/couchdb/EntityRelationship。

另一方面,Riak 拥有建立关系的工具。它是链接。您可以将链接(此处为注释)文档的地址输入到“根”文档(此处为用户文档)。它有一个技巧。如果它是分发的,它可能会一次在多个位置进行修改。它会导致冲突,结果是巨大的矢量时钟树:/ ..不是那么糟糕,不是那么好。

Riak 还有另一个“机制”。它有 2 层键名空间,即桶和键。因此,以学生为例,如果我们有俱乐部 A、B 和 C 以及学生 StudentX、StudentY,您可以保持以下约定:

 Key = ClubA, StudentX, Value = true , 
 Key = ClubB, StudentX, Value = true , 
 Key = ClubA, StudentY, Value = true 

要读取关系,只需列出给定存储桶中的键。那有什么问题?这该死的慢。列出存储桶从来都不是 riak 的优先事项。它越来越好。顺便提一句。您不会浪费内存,因为此示例 true 可以链接到 StudentX 或 Y 的单个完整配置文件(此处不可能发生冲突)。

如您所见,NoSQL != NoSQL。您需要查看具体的实现并自己进行测试。

在列存储看起来很适合关系之前提到过..但这一切都取决于您的 A 和 C 和 P 需求;)如果您不需要 A 并且您的字节数少于 Peta 就离开它,继续使用 mysql或 Postgres。

祝你好运

【讨论】:

Riak 最近发布了 v1.0,在使用 LevelDB 后端时增加了对二级索引的支持。非常有价值的功能。【参考方案3】:

关于如何以“NoSQL 方式”存储多对多关联的所有答案都归结为同一件事:冗余存储数据。

在 NoSQL 中,您不会根据数据实体之间的关系来设计数据库。您根据将针对它运行的查询来设计数据库。使用与非规范化关系数据库相同的标准:如果数据具有凝聚力更重要(考虑以逗号分隔的列表而不是规范化表中的值),那么就这样做。

但这不可避免地会优化一种类型的查询(例如,任何用户对给定文章的 cmets),而牺牲其他类型的查询(给定用户对任何文章的 cmets)。如果您的应用程序需要对两种类型的查询进行同等优化,则不应进行非规范化。同样,如果您需要以关系方式使用数据,则不应使用 NoSQL 解决方案。

非规范化和冗余存在冗余数据集彼此不同步的风险。这称为异常。当您使用规范化的关系数据库时,RDBMS 可以防止异常。在非规范化数据库或 NoSQL 中,编写应用程序代码以防止异常成为您的责任。

有人可能会认为,让 NoSQL 数据库为您完成防止异常的艰苦工作会很棒。有一种范式可以做到这一点——关系范式。

【讨论】:

“如果您需要以关系方式使用数据,则不应使用 NoSQL 解决方案” - 那么其他运行 NoSQL 的人如何摆脱它呢?当您第一次设计应用程序时,您怎么可能知道查询数据的所有方式? Fox 示例,我可能想要最近的 cmets、用户的 cmets、标签的 cmets、给定帖子的 cmets、标记为垃圾邮件的 cmets、活动的 cmets、评分最高的 cmets 等。 完全正确——没有 NoSQL 的拥护者喜欢声称的“它只是工作”这样的事情。要么为关系数据建模预先进行大量分析,要么为最优先的查询预先进行大量分析,或者在发现设计的哪些部分时在整个项目中进行大量昂贵的重构事先没有得到足够的分析。 如果我们冗余存储数据,我们应该如何更新?比如改了名字,写了一些cmets。他的名字在user集合里已经改了,但是怎么把cmets集合里所有多余的名字都改掉呢? @M98,啊,你发现了这个策略的弱点。您必须了解所有需要更新的地方,然后在您的应用程序中编写代码以在更新任何地方时更新所有这些地方。祝你好运! 非规范化关系数据库也存在同样的问题。【参考方案4】:

你有

"user": 
    "userid": "unique value",
    "category": "student",
    "metainfo": "yada yada yada",
    "clubs": ["archery", "kendo"]


"comments": 
    "commentid": "unique value",
    "pageid": "unique value",
    "post-time": "ISO Date",
    "userid": "OP id -> THIS IS IMPORTANT"


"page": 
    "pageid": "unique value",
    "post-time": "ISO Date",
    "op-id": "user id",
    "tag": ["abc", "zxcv", "qwer"]

在关系数据库中,正常的做法是在一对多关系中对数据进行规范化。这与您在 NoSQL 数据库中所做的事情相同。只需索引您将用来获取信息的字段。

例如,对你来说重要的索引是

Comment.UserID Comment.PageID Comment.PostTime Page.Tag[]

如果您使用NosDB (A .NET based NoSQL Database with SQL support),您的查询将类似于

 SELECT * FROM Comments WHERE userid = ‘That user’;

 SELECT * FROM Comments WHERE pageid = ‘That user’;

 SELECT * FROM Comments WHERE post-time > DateTime('2016, 1, 1');

 SELECT * FROM Page WHERE tag = 'kendo'

从他们的SQL cheat sheet 或文档中检查所有支持的查询类型。

【讨论】:

【参考方案5】:

尽管在这种情况下最好使用 RDBMS 而不是 NoSQL,但一种可能的解决方案是维护额外的节点或集合来管理映射和索引。它可能会以额外的集合/节点和处理的形式产生额外的成本,但它会提供一种易于维护并避免数据冗余的解决方案。

【讨论】:

以上是关于你如何在 NoSQL 中跟踪记录关系?的主要内容,如果未能解决你的问题,请参考以下文章

你应该使用NoSQL数据库SQL数据库还是两者都用?

如何正确选择NoSQL数据库?选型要点有哪些?

NoSQL: 如何在 Ubuntu 16.04 上安装 OrientDB

Redis这些知识你了解吗?

NoSQL、ElasticSearch 甚至是老式的关系数据库?

如何使用AND条件选择多对多关系中的记录[重复]