19

假设我想存储我的应用程序的用户之间的关系,类似于 Facebook,本身。

这意味着如果A是B的朋友(或某种关系),那么B也是A的朋友。为了存储这种关系,我目前计划将它们存储在关系表中,如下所示

  UID      FriendID
 ------    --------
 user1      user2
 user1      user3
 user2      user1

但是我在这里面临两个选择:

  1. 典型情况下,我将同时存储user1 -> user2user2->user1。这将占用更多空间,但(至少在我的脑海中)只需要一次通过行来显示特定用户的朋友。
  2. 另一种选择是存储user1->user2ORuser2->user1并且每当我想查找 的所有朋友时user1,我都会查询表的两列以查找用户的朋友。它将占用一半的空间,但(至少在我的脑海中)两倍的时间。

首先,我的推理是否恰当?如果是,那么我是否忘记了任何瓶颈(在扩展/吞吐量或其他方面)?

基本上,除了这里列出的之外,两者之间是否有任何权衡。另外,在工业中,一个比另一个更受欢迎吗?

4

3 回答 3

23

以下是这两种方法在数据库中的物理表示方式:

在此处输入图像描述

让我们分析这两种方法......

方法1(两个方向都存储在表中):

  • PRO:更简单的查询。
  • CON:仅插入/更新/删除一个方向可能会损坏数据。
  • MINOR PRO:不需要额外的限制来确保友谊不能被复制。
  • 需要进一步分析:
    1. TIE:一个索引涵盖两个方向,因此您不需要二级索引。
    2. TIE:存储要求。
    3. 领带:性能。

方法2(表中只存储一个方向):

  • CON:更复杂的查询。
  • PRO:不会因为忘记处理相反的方向而损坏数据,因为没有相反的方向
  • MINOR CON: 需要CHECK(UID < FriendID),所以相同的友谊永远不能用两种不同的方式来表示,而钥匙(UID, FriendID)可以完成它的工作。
  • 需要进一步分析:
    1. TIE:需要两个索引来覆盖查询的两个方向(复合索引 on{UID, FriendID}和复合索引 on {FriendID, UID})。
    2. TIE:存储要求。
    3. 领带:性能。

1 点特别有趣。MySQL/InnoDB总是对数据进行 集群,而在集群表中二级索引可能很昂贵(请参阅本文中的“集群的缺点” ),因此方法 2 中的二级索引似乎会吃掉更少行的所有优势. 然而,二级索引包含与主索引完全相同的字段(仅以相反的顺序),因此在这种特殊情况下没有存储开销。也没有指向表堆的指针(因为没有表堆),所以它可能比普通的基于堆的索引更便宜。并且假设查询被索引覆盖,通常也不会有与聚集表中的二级索引相关联的双重查找。因此,这基本上是平局(方法 1 和方法 2 都没有显着优势)。

2 点与第 1 点相关:无论我们是拥有 N 个值的 B-Tree 还是拥有 N/2 个值的两个 B-Tree,都无关紧要。所以这也是一个平局:两种方法都将使用大约相同数量的存储空间。

同样的推理也适用于第 3 点:无论我们搜索一个较大的 B-Tree 还是搜索两个较小的 B-Tree,都没有太大区别,所以这也是平局。

因此,为了稳健性,尽管查询有些丑陋并且需要额外的CHECK,我还是采用方法 2。

于 2012-05-30T11:46:41.550 回答
5

这些天存储相对便宜,所以我不会因此而担心。

我担心的是你现在必须清理,因为你存储了两次信息。因此,如果您“取消好友”某人,则必须删除 2 条记录,而不仅仅是一条。

其他考虑因素是搜索和索引。如果您遵循一致的约定(例如始终在散列之前将较高的 id 附加到较低的 id),则对 2 个用户 id 的组合进行散列以检查是否存在可能会有优势。

所以现在你有了其他的可能性。您是否有兴趣查询这 2 个用户之间的关系?还是看一个用户的属性更重要?

这些是对系统将做什么的担忧。查看 DDD(领域驱动设计)和 CQRS(命令查询职责分离)等主题,了解如何划分您的应用程序,以便以最简单的方式实现每个区域。这将为您提供稍后微调和优化的途径,而不会遇到复杂性问题。

于 2012-05-29T23:25:00.053 回答
1

Branko Dimitrijevic的选项中选择选项 1 和选项 2 时,您应该考虑这一点:

您尝试设计的关系是对称的还是不对称的?

例如(不好的例子,但仍然说明了我的观点)如果您只是想知道两个用户是家人还是朋友,那么链接是对称的。如果一个用户是另一个家庭的成员,则反之亦然。可以考虑方法 2。

但是,如果您想要更具体的信息,例如一个人对另一个人是什么类型的家庭(是他们的父亲、儿子、叔叔吗?),那么它就变得不对称了。如果 A 是 B 的父亲,那么 B 是 A 的儿子/女儿。方法 1 可能成为必要。

于 2019-02-18T11:32:09.010 回答