假设我想在我的应用程序的用户之间存储关系,类似于Facebook本身。
这意味着如果 A 是 B 的朋友(或某种关系),那么 B 也是 A的朋友。为了存储这种关系,我目前正计划将它们存储在关系表中,如下所示
UID FriendID
------ --------
user1 user2
user1 user3
user2 user1
但是我在这里面临两个选择:
user1 -> user2
和user2->user1
。这将占用更多空间,但(至少在我的脑海中)只需要一行通过行来显示特定用户的朋友。user1->user2
或user2->user1
,每当我想找到user1
的所有朋友时,我会查询表的两列以查找用户的朋友。它将花费一半的空间,但(至少在我的脑海中)是两倍的时间。首先,我的推理是否恰当?如果是,那么我是否会忘记任何瓶颈(在扩展/吞吐量或其他方面)?
基本上,除了这里列出的那些之外,两者之间是否存在任何权衡。此外,在工业中是一个优先于另一个?
答案 0 :(得分:18)
以下是这两种方法在数据库中的物理表示方式:
让我们分析两种方法......
方法1(表中存储的两个方向):
方法2(表中只存储一个方向):
CHECK(UID < FriendID)
,所以永远不能用两种不同的方式表达同一种友谊,(UID, FriendID)
上的关键字可以完成它的工作。{UID, FriendID}
上的综合索引和{FriendID, UID}
上的综合索引)。第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可能有必要。