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

**核心需求**

  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 小时前
分布式微服务系统架构第152集:JavaPlus技术文档平台日更
分布式·微服务·云原生·架构·系统架构
蓝天居士2 小时前
软考 系统架构设计师系列知识点之杂项集萃(93)
系统架构
半夏知半秋1 天前
skynet源码学习-skynet_env守护进程
服务器·学习·系统架构
掘金-我是哪吒2 天前
分布式微服务系统架构第150集:JavaPlus技术文档平台日更
分布式·微服务·云原生·架构·系统架构
GineLee2 天前
吉林大学计算机组成原理期末复习简答题整理
系统架构·硬件架构·改行学it
慧一居士2 天前
VS和VS Code 对比和区别
系统架构
掘金-我是哪吒2 天前
分布式微服务系统架构第148集:JavaPlus技术文档平台日更
分布式·微服务·云原生·架构·系统架构
蓝天居士4 天前
软考 系统架构设计师系列知识点之杂项集萃(89)
系统架构
蓝天居士4 天前
软考 系统架构设计师系列知识点之杂项集萃(90)
系统架构