# 设计一个关注人和被关注人的场景,怎么更合理更快的查询? ## 一、明确需求(面试官第一步要听到) 关注关系模型一般有两个典型查询: 1. **查询 A 关注了哪些人(关注列表)** 2. **查询有哪些人关注了 A(粉丝列表)** 3. **判断 A 是否关注 B** 4. 关注、取消关注的写操作需要高性能 5. 热门用户粉丝量可能非常巨大(百万级) 因此设计必须满足: * 高频读(关注/粉丝列表) * 高频写(关注/取关) * 热点用户不致“查询雪崩” * 水平扩展支持(可能是千万级社交关系) --- ## 二、Basic:关系型数据库(MySQL)基础设计 ### 1. 关注表 follow(follower, following) 最基础的表设计: ``` CREATE TABLE follow ( follower_id BIGINT NOT NULL, following_id BIGINT NOT NULL, created_at DATETIME, PRIMARY KEY (follower_id, following_id), INDEX idx_following (following_id, follower_id) ); ``` #### 能解决的查询: * 查关注列表:`where follower_id = ?` * 查粉丝列表:`where following_id = ?` * 判断是否关注:`select 1 from follow where follower_id = ? and following_id = ?` **注意:联合索引必须成对建,查询才快。** #### 问题: * 大 V 粉丝百万、千万时,单表查询压力很大 * 分库分表如何切分?(ID 范围 or hash) * 热点用户查询容易成为单库热点 --- ## 三、中级:读写分离 + 缓存 + 反范式 如果粉丝量大,MySQL 不够用,可以采用: ### 解决方案 1:Redis 缓存关注列表/粉丝列表(最常见) 为每个用户维护两个集合: ``` redis set: user:{id}:following redis set: user:{id}:followers ``` 查询速度:O(1) 优点: * 查询极快 * 判断是否关注极快 (`SISMEMBER`) * 热点用户不再打数据库 缺点: * 内存占用大 * 需要“数据同步”策略(写双份:mysql + redis) #### 常见写法(Lua 保证原子性): * 关注:`SADD following/set` + `SADD followers/set` * 取关:`SREM following/set` + `SREM followers/set` MySQL 作为最终一致性存储。 --- ### 解决方案 2:反范式,提前维护计数 为每个人维护关注数、粉丝数: ``` user.following_count user.followers_count ``` 避免 count(*) 查询,对性能提升巨大。 --- ## 四、高级:分库分表 + 按用户水平拆分 当用户量达到 **上亿**,关注表可能达到 **百亿级**,必须分库分表。 ### 策略: #### 1. 按 follower_id 分库分表 优点: 查“关注列表”非常快,可做到单表查询+索引命中 缺点: 查“粉丝列表”需要扫多个分片(粉丝查询最难做) --- #### 2. 按 following_id 分库分表 优点: 查“粉丝列表”非常快 缺点: 查关注列表须跨分片 --- #### 3. 双写 + 双分片(最稳,但成本最高) * follow_by_follower 表(按 follower_id 分片) * follow_by_following 表(按 following_id 分片) 写入两张表,查询各自命中自己的表。 这是大厂(微博、抖音)常用的方式。 优点: * 查询两端都快 * 热点用户不会打单表 缺点: * 写放大(2 次写入) * 双表同步复杂 --- ## 五、资深工程师级:图数据库 / KV 模型 #### 方案:使用图数据库(Neo4j / JanusGraph) 优势: * 天然支持节点和边的关系查询 * 范围查询 + 深度关系极强 * 避免 MySQL 的 join 复杂度 缺点: * 写 QPS 不如 MySQL * 分布式能力依赖厂商 图数据库适合“多跳关系”,比如“好友的好友”。 --- ## 六、资深架构师级:消息流 + 异步构建粉丝倒排表 取关/关注操作写入消息队列(Kafka): ``` topic: follow-event { follower_id, following_id, action: follow/unfollow, timestamp } ``` 后台异步构建: * Redis 缓存倒排表 * ElasticSearch 关注关系搜索索引(支持分页、排序) * HBase / Cassandra 存储海量关系 优点: * 写入快 * 读侧可多种存储模型扩展 * 极致水平扩展 缺点: * 强一致性弱 * 系统复杂度更高 --- ## 七、总结 > 关注关系本质是一个巨大规模的“关系边存储 + 两端倒排索引”问题。 > > 小规模使用 MySQL 单表 + 单列/联合索引。 > > 中规模使用 Redis 缓存倒排表解决高 QPS 读写、加上反范式计数。 > > 大规模(微博级)通过分库分表 + 双分片(按 follower_id 和 following_id 各建一个分片集)处理关注和粉丝查询。 > > 超大规模可以引入 Kafka 做关注事件流,异步构建粉丝倒排索引,并基于 Redis/HBase/ES 做查询。 > > > 目标是:读快、写快、扩展好、热点用户不打穿单点。