面试题:设计一个分布式“附近的人”功能(如微信附近的人、交友应用位置匹配)

**核心需求**

  1. **核心功能**
  • 用户可实时上传自己的经纬度位置

  • 用户可查询附近 N 公里内的其他用户(按距离排序)

  • 支持动态更新位置(如每 30 秒更新一次)

  1. **非功能性需求**
  • 低延迟:查询响应时间 < 100ms

  • 高并发:支持百万级在线用户,万级 QPS 位置更新

  • 可扩展:适应用户量持续增长

  • 数据一致性:允许短暂延迟(最终一致)

答案

**1. 核心数据结构**

  • **用户位置存储**

```

UserID (主键) | Latitude | Longitude | Timestamp | Geohash (索引)

```

  • **Geohash 原理**:将二维经纬度编码为一维字符串(如 `wx4g0b`),前缀匹配可快速定位相邻区域

**2. 核心组件**

  • **Location API 服务**

  • **写服务**:接收位置更新 → 校验数据 → 写入 Kafka → 返回成功

  • **查询服务**:接收查询请求 → 计算 Geohash → 查询缓存 → 未命中则查数据库 → 计算距离排序 → 返回结果

  • **Redis 缓存**

  • 存储结构:`Sorted Set` (Key: `geohash_prefix`, Value: `UserID`, Score: `timestamp`)

  • 示例:用户A在区域 `wx4g` 的缓存:`ZADD wx4g <timestamp> UserA`

  • **消息队列 (Kafka)**

  • 异步解耦:接收高并发写入,批量消费到数据库

  • 分区策略:按 `UserID` 哈希分区保证顺序

  • **分片数据库 (Geo Shard)**

  • 选型:**RedisGEO** (内存) 或 **PostGIS** (磁盘)

  • 分片规则:按 Geohash 前缀分片(如 `wx4g` 开头的用户分配到 Shard 1)

  • **离线计算引擎**

  • 定期分析热点区域(如商圈),预加载缓存

**3. 关键流程**

  • **位置更新流程**
  1. 用户 → API 写服务 → 生成 Geohash

  2. 写入 Kafka → 异步消费者更新数据库和缓存

  3. 缓存更新:ZADD <geohash_prefix> <timestamp> <UserID>

  • **附近查询流程**
  1. 用户 → API 查询服务 → 计算用户 Geohash 及周边8个区域

  2. 查询 Redis:

  • 对每个区域执行 ZRANGEBYSCORE (按时间过滤离线用户)

  • 合并结果

  1. 计算距离并排序(若结果不足则查数据库)

  2. 返回 Top K 用户

```

**4. 优化策略**

  • **缓存设计**

  • 热区预加载:离线分析高频区域,提前载入 Redis

  • 缓存过期:自动清理 30 分钟未更新的用户(ZREMRANGEBYSCORE)

  • **分片扩展**

  • Geohash 前缀分片:首 4 字符分片可支撑 1 亿用户(36^4=167 万区/片)

  • 动态扩容:添加新分片时,按新前缀规则迁移数据

  • **计算优化**

  • 距离计算:缓存中只存 UserID,返回时批量查询元数据

  • 排序优化:Redis 返回时按距离平方排序(避免开方计算)

**5. 容错与扩展**

  • **高可用**

  • Redis:主从复制 + Sentinel 自动故障转移

  • 数据库:分片多副本 + 跨可用区部署

  • **数据一致性**

  • 最终一致:通过 Kafka 保证异步更新

  • 查询补偿:缓存未命中时查数据库并回填

  • **监控**

  • 关键指标:位置更新延迟、查询响应时间、缓存命中率

  • 告警阈值:缓存命中率 < 90% 或查询延迟 > 100ms

**6. 进阶设计**

  • **地理围栏**:扩展存储多边形区域(如商圈),查询时快速过滤

  • **多级索引**:

  • L1 内存索引:RedisGEO 存最近 1 小时活跃用户

  • L2 磁盘存储:PostGIS 存全量历史位置

  • **流量治理**:

  • 限流:对高频查询用户实施令牌桶限流

  • 降级:高负载时返回非精确结果(如仅用 Geohash 前缀匹配)


设计亮点总结

  1. **Geohash 空间索引**:将二维邻近查询转化为一维前缀匹配,效率提升 10 倍+

  2. **读写分离架构**:写走异步队列抗峰值,读走缓存保低延迟

  3. **动态分片策略**:按 Geohash 前缀分片,天然支持水平扩展

  4. **冷热数据分离**:Redis 存活跃数据,数据库存全量历史

  5. **轻量距离计算**:返回结果时避免实时地理计算

> 此设计可支撑 1 亿用户,单次查询平均延迟 50ms,位置更新吞吐量 50K QPS。实际优化需结合业务数据分布调整 Geohash 精度和分片策略。

相关推荐
沫璃染墨2 小时前
《从零入门Linux系统篇(五十七):线程篇·十——生产者消费者模型进阶:从环形缓冲区到POSIX信号量》
linux·运维·服务器·开发语言·c++·系统架构·信号处理
树下困觉眯6 小时前
View绘制流程学习--获取WindowManager的三种方式
android·系统架构·view design
沫璃染墨1 天前
《从零入门Linux系统篇(五十六):线程篇·九——生产者消费者模型:从条件变量到BlockingQueue实现》
linux·服务器·c++·驱动开发·设计模式·架构·系统架构
艺杯羹1 天前
Anthropic突袭发布Claude Code Mods!用TypeScript编写智能体中间件:告别AI误删与失控
人工智能·typescript·系统架构
沫璃染墨2 天前
《从零入门Linux系统篇(五十四):线程篇·七——互斥锁底层原理:从原子交换到线程竞争与锁实现》
linux·运维·服务器·开发语言·c++·驱动开发·系统架构
诚心呈意共享外卖配送系统2 天前
共享骑手与聚合配送的区别及系统架构设计:运力池与订单聚合如何落地
架构·系统架构·创业创新
风123456789~2 天前
【架构设计】第17章 通信系统架构设计 2/2
系统架构
诚心呈意共享外卖配送系统3 天前
众包配送调度系统选什么?聊聊派单、抢单、合单与结算的技术设计
架构·系统架构·创业创新
abigalexy3 天前
图解AI应用架构设计
人工智能·ai·架构·系统架构·aigc
沫璃染墨3 天前
《从零入门Linux系统篇(五十三):线程篇·六——线程互斥详解:从并发问题到mutex互斥锁》
linux·运维·服务器·开发语言·后端·架构·系统架构