如何在像MySQL这样的RDBMS中存储双向关系?

时间:2012-05-29 22:58:03

标签: mysql database-design relational-database relationship

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

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

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

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

  1. 典型案例,我将同时存储user1 -> user2user2->user1。这将占用更多空间,但(至少在我的脑海中)只需要一行通过行来显示特定用户的朋友。
  2. 另一种选择是存储user1->user2user2->user1,每当我想找到user1的所有朋友时,我会查询表的两列以查找用户的朋友。它将花费一半的空间,但(至少在我的脑海中)是两倍的时间。
  3. 首先,我的推理是否恰当?如果是,那么我是否会忘记任何瓶颈(在扩展/吞吐量或其他方面)?

    基本上,除了这里列出的那些之外,两者之间是否存在任何权衡。此外,在工业中是一个优先于另一个?

3 个答案:

答案 0 :(得分:18)

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

enter image description here

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

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

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

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

  • CON:更复杂的查询。
  • PRO:忘记处理相反的方向不能破坏数据,因为没有相反的方向
  • MINOR CON:需要CHECK(UID < FriendID),所以永远不能用两种不同的方式表达同一种友谊,(UID, FriendID)上的关键字可以完成它的工作。
  • 需要进一步分析:
    1. TIE:cover两个查询方向都需要两个索引({UID, FriendID}上的综合索引和{FriendID, UID}上的综合索引)。
    2. TIE:存储要求。
    3. TIE:表现。

第1点特别感兴趣。 MySQL / InnoDB 总是 clusters数据,并且在集群表中二级索引可能很昂贵(参见this article中的“集群的缺点”),所以它看起来好像是次要的方法2中的索引会占用较少行的所有优点。 ,辅助索引包含与主要字段完全相同的字段(仅在相反的顺序中),因此在此特定情况下不存在存储开销。还没有指向表堆的指针(因为没有表堆),所以它可能比普通的基于堆的索引更便宜。并且假设查询被索引覆盖,则通常不会在群集表中与二级索引关联的双重查找。所以,这基本上是一个平局(方法1和方法2都没有显着优势)。

点2 与点1有关:我们是否会有一个N树的B树或两个B树,每个都有N / 2值并不重要。所以这也是一个平局:两种方法都会消耗大约相同的存储量。

同样的推理适用于第3点:我们是搜索一个更大的B树还是两个更小的B树,没有太大差别,所以这也是一个平局。

因此,对于稳健性,尽管有些丑陋的查询和需要额外的CHECK,我仍然采用方法2。

答案 1 :(得分:3)

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

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

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

所以现在你有其他可能性。您是否对查询2个用户之间的关系感兴趣?或者,查看一个用户的属性更重要吗?

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

答案 2 :(得分:0)

Branko Dimitrijevic的选项中选择选项1和选项2时,您应该考虑以下几点:

您是要设计对称关系还是非对称关系?

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

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