Skip to content

P14 给你一个亿 Redis keys 统计双方的共同好友 ​

面试题:用户好友关系用 Redis 存(一亿个 key),怎么统计两个用户的共同好友?

存储方案 ​

每个用户一个 Set 存好友 ID:

text
key:friends:{userId}
value:set of 好友 userId

统计共同好友 ​

交集运算:

bash
SINTER friends:10001 friends:10002

SINTER 直接返回两个集合的交集,即共同好友。如果只要数量:

bash
SINTERCARD friends:10001 friends:10002   # Redis 7.0+

或 SINTERSTORE 把结果存到临时 key 再 SCARD。

为什么用 Set ​

  • 好友关系天然是集合,无重复、无序;
  • Set 底层是哈希表/整数集合,SADD/SREM/SISMEMBER 都是 O(1);
  • Redis 的集合运算在服务端完成,不用把数据拉到客户端,网络开销小。

大规模优化点 ​

1. 大 key 问题 ​

一亿用户意味着可能有超大 Set(大 V 用户几百万好友)。大 key 会造成:

  • 单次操作耗时增加、阻塞 Redis 单线程;
  • 内存倾斜、迁移/扩容困难。

对策:

  • 分片存储:好友按 userId 取模分桶(friends:{uid}:{bucket}),统计时对多个分片分别 SINTER 再合并;
  • 限制单用户好友上限或分层(普通好友/特别关注分开存);
  • 用 SINTERCARD 只算数量不取结果,避免大集合交集的网络传输。

2. 热点统计缓存 ​

高频查询的共同好友结果缓存(TTL 短一点),避免重复交集计算。

3. 冷热分离 ​

活跃用户好友在 Redis,非活跃用户好友落数据库,按需加载。

加分点 ​

  • 对比方案:用数据库表 (user_id, friend_id) 查询,需要 join 或 IN 查询,量大时性能远不如 Redis 集合运算;
  • 交集可以用小集合驱动大集合:用好友数少的集合做基准,减少计算量(SINTER 内部已优化);
  • 好友关系变更要同步:SADD/SREM 增量维护,而不是全量重建。

一句话总结 ​

好友关系用 friends:{userId} 的 Set 存储,共同好友直接 SINTER 求交集;数据量大时按用户分片、用 SINTERCARD 控制开销、缓存热点结果。

基于 VitePress 重建