如何在同一张数据表中实现一对多自关联关系?
好友关系表设计方案评估与优化建议
你提出的初始方案不适用于双向好友场景,仅适合微博类单向关注的业务,不是好友功能的最优实现。
初始方案的核心问题
你设计的单向一对多friends_list结构存在两个明显缺陷:
- 若只存储单方向的映射(如A加B好友仅存
user_id=A, friend_id=B),查询B的好友列表时不能仅用select * from friends where userId = B,需要额外查询friend_id = B的记录,查询逻辑冗余且性能更差 - 若为了简化查询存储双向两条记录,会引入数据一致性风险:删除好友、更新关系状态时需要同时操作两条记录,漏操作就会出现“我好友列表里没他,但他好友列表里有我”的异常。
更优的双向好友关系设计
通用的生产级好友表采用单记录存双向关系的方案,表结构参考如下:
CREATE TABLE `friends` ( `relation_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '关系主键', `user_a_id` bigint unsigned NOT NULL COMMENT '发起方用户ID', `user_b_id` bigint unsigned NOT NULL COMMENT '接收方用户ID', `status` tinyint NOT NULL DEFAULT '0' COMMENT '关系状态:0=申请中 1=已通过 2=已拉黑 3=已删除', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '关系创建时间', `updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '状态更新时间', PRIMARY KEY (`relation_id`), UNIQUE KEY `uk_user_relation` (`least(user_a_id, user_b_id)`, `greatest(user_a_id, user_b_id)`), KEY `idx_user_a` (`user_a_id`), KEY `idx_user_b` (`user_b_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
设计优势:
- 唯一约束强制两个用户之间仅存一条关系记录,完全避免重复脏数据,也不存在一致性问题
- 支持好友申请、拉黑、单向删除等扩展场景,比纯映射结构灵活性高很多
- 查询逻辑简单,查询用户X的已通过好友列表的语句为:
SELECT * FROM friends WHERE (user_a_id = X OR user_b_id = X) AND status = 1
如果需要直接关联查询好友的用户信息,可使用如下语句:
SELECT u.* FROM friends f JOIN `user` u ON u.id = IF(f.user_a_id = X, f.user_b_id, f.user_a_id) WHERE (f.user_a_id = X OR f.user_b_id = X) AND f.status = 1
其他可选方案
如果你的业务是千万级以上用户量的大规模产品,可以额外做分库分表优化:按用户ID哈希分表,将同一个用户关联的所有好友关系落到同一个分片,进一步提升查询效率,中小体量产品不需要额外改造。
内容的提问来源于stack exchange,提问作者craftdeer
相关产品推荐
相关产品推荐

