21

我的网站有一个追随者/追随系统(如 Twitter)。我的困境是创建数据库结构来处理谁在关注谁。

我想出的是创建一个这样的表:

 id  |  user_id  |  followers |  following
  1  |    20     |  23,58,84  |  11,156,27
  2  |    21     |  72,35,14  |  6,98,44,12
 ... |   ...     |    ...     |     ...

基本上,我在想每个用户都会有一行包含他们的关注者和他们关注的用户的列。关注者和他们关注的人将使用逗号分隔他们的用户 ID。

这是一种有效的处理方式吗?如果没有,最好的选择是什么?

4

4 回答 4

54

That's the worst way to do it. It's against normalization. Have 2 seperate tables. Users and User_Followers. Users will store user information. User_Followers will be like this:

id | user_id | follower_id
1  | 20      | 45
2  | 20      | 53
3  | 32      | 20

User_Id and Follower_Id's will be foreign keys referring the Id column in the Users table.

于 2013-11-01T19:43:05.997 回答
17

到目前为止,有一个比其他答案提出的更好的物理结构:

CREATE TABLE follower (
    user_id INT, -- References user.
    follower_id INT,  -- References user.
    PRIMARY KEY (user_id, follower_id),
    UNIQUE INDEX (follower_id, user_id)
);

InnoDB 表是clustered,因此二级索引的行为与基于堆的表不同,如果您没有意识到这一点,可能会产生意想不到的开销。拥有代理主键id只会无缘无故地添加另一个索引1并使 {user_id, follower_id} 和 {follower_id, user_id} 上的索引比它们需要的更胖(因为聚集表中的二级索引隐含地包含 PK 的副本)。

上表没有代理键 id,并且(假设 InnoDB)物理上由两棵 B-Tree(一个用于主/集群键,一个用于辅助索引)表示,这与双向搜索的效率差不多2 . 如果你只需要一个方向,你可以放弃二级索引,只需要一个 B-Tree。

顺便说一句,您所做的违反了原子性原则,因此违反了 1NF。


1每个额外的索引都会占用空间,降低缓存效率并影响 INSERT/UPDATE/DELETE 性能。

2从被追随者到追随者,反之亦然。

于 2013-11-02T05:03:02.350 回答
6

该表示的一个弱点是每个关系都被编码两次:一次在一行中用于关注者,一次在行中用于后续用户,这使得维护数据完整性变得更加困难并且更新乏味。

我会为用户制作一张表格,为关系制作一张表格。关系表如下所示:

id | follower | following
1  | 23       | 20
2  | 58       | 20
3  | 84       | 20
4  | 20       | 11
...

这种方式添加新关系只是一个插入,删除关系是一个删除。汇总计数以确定给定用户拥有多少关注者也更容易。

于 2013-11-01T19:45:25.707 回答
3

不,您描述的方法有一些问题。

首先,将多个数据点存储为逗号分隔的字符串存在许多问题。加入起来很困难(虽然你可以加入,但使用like它会降低性能),搜索起来又困难又慢,而且无法按照你想要的方式建立索引。

其次,如果同时存储关注者列表和关注者列表,则会出现冗余数据(A 关注 B 的事实会出现在两个地方),既浪费空间,也创造了潜在的数据不同步(如果数据库在 B 的关注者列表中显示 A,但在 A 的关注者列表中未显示 B,则数据不一致,很难从中恢复)。

相反,请使用连接表。这是一个单独的表,其中每一行都有一个用户 ID 和一个关注者 ID。这允许将事物存储在一个地方,允许索引和连接,还允许您向该行添加其他列,例如显示以下关系何时开始。

于 2013-11-01T19:46:34.323 回答