基于Firebase实现用户拉黑功能及查询优化的技术咨询
嘿,这个场景我太熟悉了——做附近陌生人社交类应用,拉黑/屏蔽逻辑的性能优化绝对是绕不开的坎,尤其是你这种需要频繁校验双向拉黑关系的场景,直接查其他用户的拉黑列表完全是死路一条,用户量上来分分钟把数据库打崩。给你几个经过实战验证的解决方案,按优先级排序:
核心解决方案思路
1. 重构拉黑关系的存储结构,反向存储"被拉黑"关系
你现在的问题根源在于,要判断"谁拉黑了我",需要遍历对方的拉黑列表,这是O(n)的低效操作。我们可以把拉黑关系双向存储:
- 当用户A拉黑用户B时,同时写入两条记录:
blocked_list:A→ 存储A拉黑的所有用户ID(用于过滤A要发送消息的对象)blocked_by_list:B→ 存储所有拉黑B的用户ID(用于快速判断哪些用户不能给B发消息)
具体实现(以Redis为例,比关系型数据库更适合这种高频读写场景):
# 用户123拉黑用户456 SADD user:123:blocked 456 SADD user:456:blocked_by 123 # 用户123取消拉黑用户456 SREM user:123:blocked 456 SREM user:456:blocked_by 123
这样你的消息发送逻辑就可以改成:
- 随机获取10位附近用户ID列表
candidates - 从
user:current_user:blocked中获取自己拉黑的用户集合my_blocked,先从candidates中排除这些ID - 对剩余的候选用户,批量检查他们的
blocked_by集合是否包含当前用户ID(也就是判断对方有没有拉黑我) - 从最终过滤后的列表里随机选一位发送消息
2. 用批量查询代替循环单查,减少IO次数
不要对每个候选用户单独查一次拉黑关系,用批量命令一次性搞定:
示例(Python+Redis批量校验):
import redis import random r = redis.Redis(host="your_redis_host", port=6379) current_user_id = 123 candidates = [456, 789, 101112, 131415] # 随机获取的10个附近用户ID # 第一步:过滤自己拉黑的用户 my_blocked = r.smembers(f"user:{current_user_id}:blocked") filtered_candidates = [uid for uid in candidates if uid not in my_blocked] # 第二步:批量检查对方是否拉黑我 pipe = r.pipeline() for uid in filtered_candidates: pipe.sismember(f"user:{uid}:blocked_by", current_user_id) results = pipe.execute() # 过滤掉拉黑我的用户 final_candidates = [uid for uid, is_blocked in zip(filtered_candidates, results) if not is_blocked] # 随机选一位发送消息 if final_candidates: target_user = random.choice(final_candidates) # 执行消息发送逻辑...
3. 缓存预热+异步更新,降低数据库压力
如果你们用的是关系型数据库存储拉黑关系(比如MySQL),一定要在Redis做缓存:
- 用户登录/上线时,把自己的
blocked和blocked_by列表同步到Redis - 当用户拉黑/取消拉黑时,先更新数据库,再异步更新Redis(用消息队列比如RabbitMQ/Kafka,或者直接Redis事务)
- 给缓存设置合理的过期时间(比如1小时),同时允许主动触发更新(比如用户修改拉黑列表时立即更新)
4. 预计算候选池,减少实时过滤开销
如果附近用户的数量足够多,可以提前预计算符合条件的用户池:
- 后台异步任务定期(比如每5分钟)给每个用户生成一个"可发送消息的附近用户列表",过滤掉自己拉黑的和拉黑自己的用户
- 发送消息时直接从这个预计算的列表里随机选,不用实时过滤
- 注意:当用户修改拉黑列表时,要立即触发该用户和被拉黑用户的候选池更新
5. 退而求其次:放宽过滤逻辑(如果性能压力实在太大)
如果以上方案都暂时没法落地,可以考虑折中:
- 先过滤自己拉黑的用户,然后随机选一位,发送前再检查对方是否拉黑我
- 如果对方拉黑了我,就重新选下一位,直到找到符合条件的用户
- 这种方式的好处是实现简单,但极端情况下可能会多次重试(比如10个候选全拉黑了我),适合用户拉黑率不高的场景
内容的提问来源于stack exchange,提问作者Dridia
相关产品推荐
相关产品推荐

