整体分层:接入层 → 业务服务层(用户关系 / 朋友圈动态 / 互动点赞)→ 缓存层 → 消息队列层 → 存储层(MySQL + 分库分表 + ES)
核心技术栈:SpringBoot、MyBatis-Plus、Redis Cluster、RocketMQ/Kafka、Elasticsearch、Sharding-JDBC、分布式锁 Redisson
一、模块 1:关注、取关、共同关注(用户关系子系统)
1.1 核心实体与存储设计
数据库分库分表方案
用户关系数据量大,按用户 ID 哈希分片,分为两张核心表:
-
user_follow 关注表(我关注的人)
CREATE TABLE user_follow (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT COMMENT '当前用户ID',
follow_user_id BIGINT COMMENT '被关注人ID',
status TINYINT DEFAULT 1 COMMENT '1正常关注 0已取关',
create_time DATETIME,
update_time DATETIME,
UNIQUE KEY uk_uid_fuid (user_id, follow_user_id)
) ENGINE=InnoDB; -
user_fan 粉丝表(关注我的人)
CREATE TABLE user_fan (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT COMMENT '被关注人ID',
fan_user_id BIGINT COMMENT '粉丝ID',
status TINYINT DEFAULT 1,
create_time DATETIME,
update_time DATETIME,
UNIQUE KEY uk_uid_fanuid (user_id, fan_user_id)
) ENGINE=InnoDB;
设计说明:双向存储,查关注列表查 user_follow,查粉丝列表查 user_fan,避免关联查询,提升读性能;唯一索引防止重复关注。
Redis 缓存设计(热点数据全缓存,减轻 DB 压力)
- 我关注的人集合 Set Key:
follow:set:{userId},Value:所有 follow_user_id,过期时间永久,取关时直接 SREM - 我的粉丝集合 Set Key:
fan:set:{userId},Value:所有 fan_user_id - 关注状态缓存(快速判断 A 是否关注 B) Key:
follow:map:{userId},Hash 结构,field=followUserId,value=1 关注 / 0 取关
1.2 核心接口逻辑
- 关注接口 follow (Long targetUserId)
- 前置校验:不能关注自己;分布式锁防止并发重复关注
- DB:user_follow 插入 / 更新,user_fan 插入 / 更新
- Redis:SADD 关注集合、HSET 状态
- MQ 发送关注关系变更事件,用于后续动态推送刷新共同好友
- 取关接口 unFollow (Long targetUserId)
- DB:两张表 status 更新为 0,逻辑删除(保留历史数据)
- Redis:SREM 集合、HSET 状态 0
- 共同关注(双向交集) 需求:查看我和用户 A 共同关注了哪些人 实现:Redis 集合交集命令
SINTER follow:set:{myId} follow:set:{targetId}优化:交集结果缓存 5 分钟,减少频繁计算;超大集合交集异步计算存入临时缓存
1.3 并发与一致性方案
- 关注 / 取关使用Redisson 分布式锁 ,锁 key:
lock:follow:{uid}:{targetUid},防止并发重复关注、脏数据 - 缓存与 DB 双写一致性:先更新 DB,再更新 Redis;失败则 MQ 重试补偿
- 冷热分离:3 个月前失效的关注 / 粉丝数据归档至历史分表,减少主表数据量
二、模块 2:关注推送(朋友圈动态流推送系统)
2.1 两种推送方案对比(业界主流)
| 方案 | 实现逻辑 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 推模式(写扩散) | 用户发朋友圈时,遍历自己所有粉丝,把动态 ID 写入每个粉丝的推送缓存 | 刷朋友圈读极快,O (1) 读取 | 大 V 粉丝百万级,发动态阻塞、占用大量内存 | 普通用户(粉丝 < 1w) |
| 拉模式(读扩散) | 用户刷朋友圈时,查询自己全部关注人,批量拉取所有人最新动态 | 发动态无性能损耗,大 V 友好 | 刷首页需要批量查询多用户动态,查询耗时高 | 百万粉大 V、网红 |
2.2 混合推送架构(线上落地最优方案)
- 用户分层:
- 普通用户(粉丝 < 5000):推模式
- 大 V(粉丝≥5000):拉模式
- 动态存储: 朋友圈动态主表
moment,按发布用户 ID 分表,ES 存储全文(文字、图片标签,支持搜索) - Redis 推送存储结构(推模式) Key:
feed:list:{userId},SortedSet 有序集合- score:动态发布时间戳(倒序,最新动态在前)
- member:momentId 限制长度:只保留最近 500 条动态,超过自动 ZREM 淘汰旧数据,控制内存占用
2.3 完整推送链路(异步解耦,核心靠 MQ)
- 用户发布朋友圈 → 写入 MySQL moment 表 + ES 索引
- 发送MomentPublishEvent消息至 RocketMQ Topic
- MQ 消费者分两条消费链路:
- 链路 1【推模式推送】:获取发布者粉丝列表,批量将 momentId 写入每个粉丝的 feed 有序集合;批量操作 pipeline 优化 Redis 性能
- 链路 2【统计异步任务】:动态点赞数、评论数初始化,更新用户动态数量缓存
- 用户刷朋友圈首页:
- 先查自身 feed 有序集合,批量获取 momentId
- 批量查询 Redis 缓存动态详情,缓存失效则批量查 MySQL
- 分页返回前端;下拉加载更多直接 ZREVRANGE 分页
2.4 关键优化点
- 推送限流熔断:大 V 误触发推模式时,熔断降级改为拉模式,防止 Redis 雪崩
- 过期动态清理:定时任务清理 feed 列表中超过 30 天的动态
- 未读红点:单独 Redis Hash 存储每个动态的未读标记,用户刷新后批量清除
三、模块 3:点赞互动子系统
3.1 存储设计(冷热分离,缓存优先)
-
数据库(持久化,记录点赞关系) 表
moment_like,按 momentId 分表(一条动态点赞量极大,按动态分片)CREATE TABLE moment_like (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
moment_id BIGINT COMMENT '朋友圈动态ID',
user_id BIGINT COMMENT '点赞用户ID',
status TINYINT DEFAULT 1 COMMENT '1点赞 0取消赞',
create_time DATETIME,
update_time DATETIME,
UNIQUE KEY uk_moment_uid (moment_id, user_id)
) ENGINE=InnoDB;
唯一索引防止同一用户重复点赞。
- Redis 缓存(所有点赞热点操作走缓存,DB 异步落库)
- 动态点赞总数:Key
like:count:{momentId}String 类型,incr/decr 原子增减 - 点赞用户集合 Set:Key
like:set:{momentId},存储所有点赞 userId,快速判断是否已点赞(SISMEMBER) - 点赞用户分页缓存:ZSet 存储点赞时间,用于前端展示点赞人列表
- 动态点赞总数:Key
3.2 点赞 / 取消赞核心流程(异步化,保证高并发)
- 点赞接口 like (Long momentId)
- Redis 原子操作:SADD 点赞集合 + INCR 点赞总数;原子命令保证计数和集合一致
- 发送 MQ 事件
MomentLikeEvent,异步消费者更新数据库 moment_like - 推送点赞通知给动态发布者(消息通知模块)
- 取消赞接口 unLike (Long momentId)
- Redis:SREM 集合 + DECR 点赞总数
- MQ 异步更新 DB 状态为 0
设计思路:点赞是高频读 + 高频写场景,同步操作只操作 Redis,DB 异步落地,保证接口 RT 极低;MQ 失败重试保证最终一致性。
3.3 高频查询需求实现
- 判断我是否点赞该动态:Redis SISMEMBER,O (1) 查询
- 获取动态点赞总数:直接读取 Redis String,无需查 DB
- 分页展示点赞用户:Redis ZREVRANGE 分页,缓存热点动态点赞列表,减少重复查询
3.4 并发与数据一致性保障
- 防止重复点赞:Redis SADD 天然幂等,重复点赞不会新增数据,接口直接返回成功
- Redis 与 DB 最终一致性:MQ 消息持久化,消费者失败重试;定时补偿任务扫描 Redis 与 DB 数据差异,修复不一致数据
- 缓存淘汰策略:冷动态(30 天无访问)清理点赞 Set 缓存,查询时降级查 DB,节省 Redis 内存
四、整体系统兜底 & 容灾方案
- 缓存雪崩 / 击穿:Redis 过期时间随机打散;点赞、feed 热点 key 永不过期;互斥锁防止缓存击穿
- 流量削峰:所有写操作(发动态、点赞、关注)全部通过 MQ 异步处理,抵御活动流量洪峰
- 降级熔断:Redis 宕机时,feed 流降级为纯拉模式;点赞缓存失效临时直接查询数据库
- 分库分表扩容:用户关系、动态、点赞表均基于 ID 哈希分片,后期数据量增长可平滑扩容分片数量