Java社区关注与粉丝关系需设计单向关注表(follower_id/followee_id)、MySQL+Redis组合存储(联合唯一索引+覆盖索引+SET缓存)、先写DB再删缓存并MQ异步重建、按followee_id分库分表,兼顾一致性与性能。

Java内容社区实现关注与粉丝关系,核心在于设计合理的社交关系模型、选择适合的存储方案,并保障数据一致性与读写性能。这不是单纯堆砌数据库表或引入消息队列就能解决的问题,而是需要结合业务场景,在模型抽象、存储选型、同步机制三方面做系统性权衡。
关注关系的数据建模:用“关注表”还是“双向边”?
最常见且推荐的做法是采用单向关系表(如 red">user_follow),记录 谁关注了谁,字段通常包括:follower_id(关注者)、followee_id(被关注者)、created_at。这种设计天然支持“我关注的人”和“关注我的人”两类查询,且避免冗余存储。
不建议为每个关注动作同时写入两条记录(即双向写),既增加写开销,又提升数据不一致风险。粉丝数、关注数等聚合指标可通过异步更新或缓存(如 Redis 的 ZSET 或 INCR)来维护,而非每次查表实时统计。
存储选型:关系型数据库 + 缓存组合最稳妥
MySQL 或 PostgreSQL 作为主存储,承担强一致性要求的操作(如关注/取关事务、防止重复关注)。关键点:
立即学习“Java免费学习笔记(深入)”;
- 联合唯一索引必须建在
(follower_id, followee_id)上,杜绝重复关注 - 高频查询(如“获取某用户最新20个粉丝”)需配合覆盖索引,例如
INDEX idx_followee_created (followee_id, created_at) - Redis 用于加速读场景:用
SET存储用户关注列表(follow:1001)、粉丝列表(follower:1001),并设置合理过期时间或通过 binlog 同步兜底
关注/取关操作的同步策略:先写DB,再刷缓存,异步补漏
典型流程应是:事务写库 → 删除对应缓存 → 发送MQ事件 → 消费端异步重建缓存+更新计数。这样兼顾一致性与响应速度。
例如用户A取关用户B:
- 在 MySQL 中删除
(A,B)记录,并提交事务 - 立刻
DEL follow:A和DEL follower:B(缓存穿透可加空值标记) - 发 MQ 消息(含 A、B ID 和操作类型),下游服务重新拉取最新关注/粉丝列表并写回 Redis
- 计数器(如
user:1001:fans_count)通过 Redis 原子指令DECR更新,或由消费端查库后重设
冷热分离与分库分表:当单表超千万行时该怎么做?
关注关系表最容易成为性能瓶颈。当 user_follow 表行数超过 2000 万,建议按 followee_id 分片(即被关注者维度哈希分库),因为“查某人粉丝”比“查某人关注了谁”更常作为首页/个人页入口,且粉丝量分布符合长尾规律(大V粉丝多,普通人少)。
分片键不宜选 follower_id,否则“我关注的人”列表会跨库,增加聚合难度。若必须支持高效双向查询,可考虑冗余一张以 follower_id 分片的表,但仅用于“我关注的人”场景,写入时双写(需事务或最终一致性保障)。
基本上就这些。模型要简洁,存储要分层,同步要留退路——Java 社区的社交关系,不复杂但容易忽略边界。










