使用 user_relationships 表存储关注关系,通过唯一约束和索引支持高效查询好友、粉丝及共同关注,拉黑操作由独立 user_blocks 表处理,确保数据一致性与逻辑清晰。

设计好友关系链数据库表结构,核心在于清晰地定义“谁关注了谁”这个行为。我个人倾向于使用一个简洁的关联表来承载所有关系,无论是双向关注还是单向粉丝模型,都能在这个基础之上灵活构建和查询。这种做法避免了冗余,也让数据模型更加纯粹。
为了实现双向关注和粉丝模型,我们可以设计一个名为
user_relationships
user_relationships
| 字段名 | 数据类型 | 约束 | 描述 |
|---|---|---|---|
| @@######@@ | @@######@@ | @@######@@, @@######@@ | 唯一标识每条关系记录 |
| @@######@@ | @@######@@ | @@######@@, @@######@@ (references @@######@@) | 关注者的用户ID |
| @@######@@ | @@######@@ | @@######@@, @@######@@ (references @@######@@) | 被关注者的用户ID |
| @@######@@ | @@######@@ | @@######@@, @@######@@ | 关系状态,例如 'active' (活跃), 'blocked' (拉黑) |
| @@######@@ | @@######@@ | @@######@@, @@######@@ | 关系创建时间 |
| @@######@@ | @@######@@ | @@######@@, @@######@@ | 关系最后更新时间 |
索引和唯一约束:
id
BIGINT
PRIMARY KEY
模型说明:
AUTO_INCREMENT
follower_id
BIGINT
NOT NULL
FOREIGN KEY
users.id
following_id
在设计关系表时,查询效率是不得不考虑的。有了上面提到的索引,我们可以相对高效地进行各种查询。
查询好友列表 (互相关注的用户): 要找到用户X的好友,我们需要找到那些用户X关注了,并且也关注了用户X的人。这通常通过一个自连接或者两次查询来实现。我个人更倾向于自连接,它在SQL层面更优雅。
BIGINT
或者,另一种更直观的自连接方式:
NOT NULL
这两种方式都能达到目的,选择哪种取决于你对SQL的偏好和实际数据库的优化情况。关键在于利用索引快速定位关系。
查询粉丝数量: 这个相对简单,直接计数即可。
FOREIGN KEY
查询关注数量: 同样是直接计数。
users.id
查询共同关注: 找出用户A和用户B共同关注了哪些人。这需要两次连接,或者更巧妙地使用
status
VARCHAR(20)
所有这些查询都高度依赖
NOT NULL
DEFAULT 'active'
在我看来,存储双向关系时,通常不需要额外的字段(如 created_at
为什么呢?
首先,数据一致性问题。如果你有一个
TIMESTAMP
NOT NULL
DEFAULT CURRENT_TIMESTAMP
updated_at
TIMESTAMP
NOT NULL
DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
UNIQUE KEY (follower_id, following_id)
INDEX (follower_id, following_id)
INDEX (following_id, follower_id)
user_relationships
(follower_id = A, following_id = B)
following_id = B
其次,查询复杂性并未显著降低。虽然
follower_id = B
(follower_id = A, following_id = B)
(follower_id = B, following_id = A)
所以,我的建议是保持数据模型的原子性:一条记录只代表一个事实——“谁关注了谁”。双向关系是两个独立事实的组合,应该通过查询来推导。这让数据模型更简洁,更新操作更安全,也更容易理解。
当然,在极端的读密集型场景下,比如一个拥有数亿用户的社交平台,每秒有数万次好友列表查询,那么可能会考虑引入缓存层(如Redis)来存储预计算的好友关系,或者使用专门的图数据库。但在关系型数据库层面,保持非冗余是最好的起点。
处理这些用户关系变更操作,我们同样可以在
is_mutual
取消关注 (Unfollow): 这是最直接的操作。当用户A取消关注用户B时,我们只需要删除
SELECT u.id, u.username -- 假设有一个users表 FROM users u JOIN user_relationships ur1 ON u.id = ur1.following_id JOIN user_relationships ur2 ON u.id = ur2.follower_id WHERE ur1.follower_id = [用户X的ID] AND ur2.following_id = [用户X的ID] AND ur1.follower_id = ur2.following_id; -- 确保是互相关注的同一个对象
SELECT T1.following_id AS friend_id FROM user_relationships T1 JOIN user_relationships T2 ON T1.follower_id = T2.following_id AND T1.following_id = T2.follower_id WHERE T1.follower_id = [用户X的ID];
这个操作非常简单,也符合我们之前强调的原子性原则。如果A和B之前是互相关注的,那么删除这条记录后,他们就不再是互相关注了(因为
SELECT COUNT(*) AS fan_count FROM user_relationships WHERE following_id = [用户X的ID] AND status = 'active';
SELECT COUNT(*) AS following_count FROM user_relationships WHERE follower_id = [用户X的ID] AND status = 'active';
拉黑 (Block): “拉黑”通常比“取消关注”要复杂一些,它通常意味着:
为了处理这种多层面的逻辑,我倾向于引入一个单独的
IN
SELECT ur1.following_id AS common_following_id FROM user_relationships ur1 JOIN user_relationships ur2 ON ur1.following_id = ur2.following_id WHERE ur1.follower_id = [用户A的ID] AND ur2.follower_id = [用户B的ID] AND ur1.status = 'active' AND ur2.status = 'active';
follower_id
following_id
| 字段名 | 数据类型 | 约束 | 描述 |
|---|---|---|---|
| @@######@@ | @@######@@ | @@######@@, @@######@@ | 唯一标识每条拉黑记录 |
| @@######@@ | @@######@@ | @@######@@, @@######@@ (references @@######@@) | 拉黑者的用户ID |
| @@######@@ | @@######@@ | @@######@@, @@######@@ (references @@######@@) | 被拉黑者的用户ID |
| @@######@@ | @@######@@ | @@######@@, @@######@@ | 拉黑时间 |
索引和唯一约束:
is_mutual
is_mutual
(A, B)
拉黑操作的实现逻辑:
当用户A拉黑用户B时,通常会执行以下步骤:
(B, A)
(A, B)
is_mutual
true
通过这种方式,我们清晰地分离了“关注”和“拉黑”这两种行为,使得业务逻辑更加清晰。
(B, A)
is_mutual
true
(B, A)
(A, B)
is_mutual
false
is_mutual
follower_id
following_id
user_relationships
user_relationships
DELETE FROM user_relationships WHERE follower_id = [用户A的ID] AND following_id = [用户B的ID];
(A, B)
(B, A)
user_blocks
user_relationships
status
user_blocks
id
BIGINT
PRIMARY KEY
AUTO_INCREMENT
blocker_id
BIGINT
NOT NULL
FOREIGN KEY
users.id
blocked_id
BIGINT
NOT NULL
FOREIGN KEY
users.id
created_at
TIMESTAMP
NOT NULL
DEFAULT CURRENT_TIMESTAMP
UNIQUE KEY (blocker_id, blocked_id)
INDEX (blocker_id, blocked_id)
INDEX (blocked_id)
user_blocks
INSERT INTO user_blocks (blocker_id, blocked_id) VALUES ([用户A的ID], [用户B的ID]);
DELETE FROM user_relationships WHERE follower_id = [用户A的ID] AND following_id = [用户B的ID];
DELETE FROM user_relationships WHERE follower_id = [用户B的ID] AND following_id = [用户A的ID];
user_blocks
user_relationships
以上就是如何设计一个好友关系链数据库表结构?(双向关注、粉丝模型)的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号