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

Ank*_*kit 14 mysql database-design relational-database relationship

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

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

  UID      FriendID
 ------    --------
 user1      user2
 user1      user3
 user2      user1
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Bra*_*vic 19

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

在此输入图像描述

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

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

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

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

  • CON:更复杂的查询.
  • PRO:由于没有相反的方向,因此忘记处理相反的方向不能破坏数据.
  • MINOR CON:需要CHECK(UID < FriendID),所以同样的友谊永远不能以两种不同的方式表现出来,关键在于(UID, FriendID)能够完成它的工作.
  • 需要进一步分析:
    1. TIE:需要两个索引来覆盖查询的两个方向(复合索引{UID, FriendID}和复合索引{FriendID, UID}).
    2. TIE:存储要求.
    3. TIE:表现.

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

点2是有关第1点:不要紧是否我们将有N个值的B树或两个B-树中,每个与N/2的值.所以这也是一个平局:两种方法都会消耗大约相同数量的存储空间.

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

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


Ada*_*ruk 5

如今,存储相对便宜,因此我不必为此担心。

让我担心的是,由于两次存储信息,您现在必须清理。因此,如果您与某人“解除好友”关系,则必须删除2条记录,而不仅仅是一条。

其他考虑因素是搜索和索引编制。假设您遵循一致的约定(例如在哈希之前始终将较高的ID附加到较低的ID),则对两个用户ID的组合进行哈希处理以检查是否存在可能会有好处。

因此,现在您还有其他可能性。您是否有兴趣查询两个用户之间的关系?还是查看一个用户的属性更重要?

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