首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >附近的人功能如何设计

附近的人功能如何设计

原创
作者头像
Java大神之路
修改2026-07-15 10:16:33
修改2026-07-15 10:16:33
70
举报
文章被收录于专栏:场景设计题场景设计题

附近的人功能如何设计

🧭 问题理解(先对齐需求)

关于“附近的人”功能,先明确几个核心点:

  • 用户量级:千万级DAU(按大厂标准聊)
  • 实时性:位置要相对新鲜,但不必强实时,可以容忍几秒到几分钟的延迟
  • 核心操作:上传坐标、搜索附近的人、分页、按距离排序
  • 隐含需求:高并发、低延迟、数据一致性不必强求,可用最终一致

核心需求拆解 📋

需求类型

具体要求

技术指标

功能需求

查看指定范围内的用户 按距离排序 支持性别 / 年龄筛选 实时更新位置

查询延迟 < 100ms 支持百万级同时在线

非功能需求

隐私保护 高并发 数据一致性 容灾备份

位置精度误差 < 50m 可用性 99.99%

整体架构设计 🏗️

核心流程:
  1. 用户上线 / 移动时,上报经纬度到位置服务
  2. 位置服务更新数据库和缓存
  3. 用户查询附近的人时,先查缓存,缓存 miss 再查数据库
  4. 结果按距离排序并过滤后返回给用户

核心技术方案 ⚙️

1. 地理位置索引:GeoHash 算法 📍

这是实现附近的人最核心的技术,将二维经纬度编码为一维字符串:

  • 编码长度越长,精度越高(6 位≈61m,7 位≈76m)
  • 相邻区域的 GeoHash 前缀相同
  • 查询时只需匹配相同前缀的用户,再计算精确距离

示例:北京天安门的 GeoHash 是wx4g0ec1,附近 500 米内的用户 GeoHash 都以wx4g0e开头。

2. 数据存储方案 💾
  • Redis Geo:首选方案,Redis 3.2 + 原生支持 Geo 命令
    • GEOADD:添加用户位置
    • GEORADIUS:查询指定半径内的用户
    • GEODIST:计算两个用户之间的距离
    • 优点:高性能、支持原子操作、自带排序
  • MySQL 空间索引:作为持久化存储
    • 使用POINT类型存储经纬度
    • 创建SPATIAL INDEX加速查询
    • 用于 Redis 缓存失效后的兜底查询
为什么首选 Redis Geo

“附近的人”本质是一个 LBS(基于位置服务)的地理位置检索,典型需求是:

  • 传入一个坐标(lat, lng)和半径 r,返回半径内的所有用户,并按距离排序。

我选 Redis Geo,原因很简单:

方案

核心数据结构

优缺点

MySQL 经纬度字段 + 球面距离公式

普通索引

需全表扫,无法利用索引,千万级直接跪 ❌

MySQL Spatial (R-tree)

空间索引

边界查询可用,但排序和距离计算仍较重,高并发扛不住 ❌

MongoDB 2dsphere

Geohash + B树

功能可以,但生态和运维成本大,调用链路长 ⚠️

Redis Geo

ZSET + Geohash

原生内存操作,读写极快,单机10w+ QPS ✅

Redis Geo 底层是把经纬度编码成 Geohash 字符串,塞进一个 Sorted Set。Score 就是这个 Geohash 转化成的 52 位整数。这样一来,附近的点,其 Geohash 前缀相同,落在 ZSET 的相邻区间,范围查询就是 ZSET 的 ZRANGEBYSCORE,时间复杂度 O(log(N)+M),非常快。

我画个简单的结构图:

3. 高性能查询优化 🚀
  • 缓存预热:热门区域提前加载用户位置到 Redis
  • 分页查询:每次只返回前 50-100 个用户,避免数据量过大
  • 结果缓存:查询结果缓存 10-30 秒,减轻数据库压力
  • 异步更新:用户位置更新通过 Kafka 异步处理,不阻塞主流程
🧠4. 核心实现:Redis 命令与编码细节

业务层就这么几条命令:

① 用户位置更新
代码语言:txt
复制
GEOADD nearby:users 116.404 39.915 user:1001

每次用户打开 App 或移动一定距离后上报,我们实时执行这条。高并发下用 Pipeline 或 Redis Cluster 的 hash tag 保证同一个用户落在相同分片。

② 搜索附近的人
代码语言:txt
复制
GEORADIUS nearby:users 116.405 39.916 5 km WITHDIST COUNT 20 ASC

返回距离、用户ID,按距离升序,带分页?Redis 没有游标,但我们用 COUNT 和客户端保存的 offset 做逻辑分页。更深层的分页不推荐,通常限制前 N 页,比如 200 条。

③ 扩展性考虑

如果用 Redis Cluster,Geo 相关的 key 必须用 hash tag 保证在一个节点:

代码语言:txt
复制
GEOADD {nearby}:users ...

搜索时也这样指定。同时我们按城市或大区域预分片,避免单个 ZSET 过大(建议一个 key 内用户数 < 50 万,过大用多 key 分片)。

📊 整体架构(带图更直观)
  • 位置服务 无状态,方便水平扩展。
  • Redis Geo 分片:比如 {beijing}:nearby:users{shanghai}:nearby:users
  • 搜索完后得到一堆 user id,批量去 Redis 缓存里拿头像昵称,缓存未命中穿透到 MySQL。
  • 用户位置更新同时异步发一条 MQ,用于轨迹存储、风控等。

进阶考虑与优化 🎯

1. 隐私保护 🔒
  • 用户可随时开启 / 关闭 "附近的人" 功能
  • 位置信息只保留最近 24 小时,过期自动删除
  • 支持模糊位置显示(如 "距离 100 米内" 而非精确坐标)
  • 禁止非好友查看用户详细位置
2. 高并发处理 ⚡
  • 读写分离:读操作走 Redis,写操作异步同步到 MySQL
  • 分片存储:按 GeoHash 前缀分片,将不同区域的用户分散到不同 Redis 节点
  • 限流保护:限制单个用户每秒查询次数,防止恶意刷接口
3. 容灾与扩展 🛡️
  • Redis 集群部署,主从复制 + 哨兵模式保证高可用
  • MySQL 主从复制,读写分离
  • 多机房部署,异地容灾
  • 支持水平扩展,用户量增加时只需添加 Redis 节点
4. 🔹分页的坑

GEORADIUS 没有服务端游标,所以若每次都传 offset 200,其实 Redis 内部会计算全部结果然后截取,浪费 CPU。解决:业务限定最大翻页,比如第 10 页之后直接拒绝;或在前端交互上做无限滚动加载一小段,同时缓存“看过的人”去重。

5. 🔹热点数据

某个城市的 Geo Key 可能成为热点,比如北京。方案:

  • 同一城市内再按地理位置做二级分片:{beijing:1}:nearby{beijing:2}:nearby,查询时根据请求坐标决定查哪个分片和相邻分片(Geohash 边界)。
  • 主从读分离,本地缓存常用搜索结果。
6. 🔹用户频繁移动

不能每次手机抖一下就更新 Redis。客户端做阈度上报:移动超过 50 米,或超过 2 分钟,才上传一次。

7. 🔹踢除离线用户

Redis 里只留活跃用户。用 TTL?不行,Geo 不能直接对 member 设过期。我们的做法:

  • 额外维护一个 online:users 的 ZSET,用最后心跳时间做 score。定时任务扫出过期用户,一并从 Geo Key 和在线 Key 中删除。
  • 或者 Geo 成员设一个“逻辑过期”时间戳在 Value 中,这里 Redis Geo 只存 member 是 user:id,不能存额外字段,所以需要另一个 key 存时间,异步清理。
8. 🧩一些深层追问的思考
Q: 如果不满足 Redis Geo,比如要在附近人中按兴趣过滤?

那就需要 Geo 提供候选集,再在应用层做交集。或用 MongoDB + 多条件索引,但会牺牲一些性能。大厂可能会自研 LBS 引擎,基于 Geohash 建立倒排索引。

Q: 数据倾斜怎么处理?

热点城市多分片,并且监控每个分片大小,动态分裂。

Q: 如何保证服务的最终一致性?

位置更新可接受短暂不一致。Redis 本身高性能,做 AOF 持久化,挂了从 RDB 恢复 + 用户心跳重建在线状态。

总结 ✨

“附近的人”核心就三句话:

  1. 用 Redis Geo 扛住海量位置读写,底层 ZSET + Geohash 精准索引。
  2. 架构上做水平分片、按区域拆分 key,避免大 key 和热点。
  3. 用户状态、分页、业务过滤都做保护,防止滥用和性能劣化。

如果需要更高维度的组合查询,可以引入 ES 的地理位置类型,但单纯一个“附近”功能,Redis Geo 是最轻量高性能的解法。

这个设计方案以Redis Geo为核心,结合 GeoHash 算法实现高效的地理位置查询,通过缓存、异步更新、分片等技术保证高并发性能,同时充分考虑了用户隐私保护和系统容灾能力。可以轻松支撑百万级同时在线用户,查询延迟控制在 100ms 以内,完全满足互联网大厂的业务需求。

附近的人功能:核心代码 + 技术难点全解 🔥

完全贴合大厂面试标准,直接可背、可写、可讲,代码简洁有亮点,难点一针见血!

核心代码(Java + Redis Geo)✨

这是面试最加分、最能体现真实开发经验的代码,基于 Spring Boot + RedisTemplate 实现。

1. 依赖(Spring Boot 环境)
代码语言:xml
复制
<!-- Spring Data Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 核心配置(Redis 连接工厂)
代码语言:java
复制
@Configuration
public class RedisConfig {
    @Bean
    public RedisGeoCommands<String, String> redisGeoCommands(RedisConnectionFactory factory) {
        return RedisTemplate.create(factory).opsForGeo();
    }
}
3. 核心业务代码(面试必背)
代码语言:java
复制
@Service
@RequiredArgsConstructor
public class NearUserService {

    private final RedisGeoCommands<String, String> redisGeoCommands;
    // Redis Key:存储所有用户位置
    private static final String GEO_KEY = "user:location";

    // ====================== 1. 上报用户位置 ======================
    public void reportUserLocation(Long userId, double longitude, double latitude) {
        // 技术亮点:使用 Point 封装经纬度
        Point point = new Point(longitude, latitude);
        // 存入 Redis Geo
        redisGeoCommands.add(GEO_KEY, point, userId.toString());
    }

    // ====================== 2. 查询附近的人(核心接口) ======================
    public List<NearUserDTO> nearByPeople(double longitude, double latitude, int radiusMeter) {
        // 1. 构造查询原点
        Point point = new Point(longitude, latitude);
        // 2. 构造距离范围(米)
        Distance distance = new Distance(radiusMeter, RedisGeoCommands.DistanceUnit.METERS);
        // 3. 构造查询参数:限制返回数量 + 携带距离
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
                .includeDistance()   // 返回距离(面试加分点)
                .sortAscending()     // 由近到远排序
                .limit(50);          // 只查50个,防止大流量

        // 4. 执行 Redis 附近查询
        GeoResults<GeoLocation<String>> results = redisGeoCommands.radius(
                GEO_KEY, point, distance, args
        );

        // 5. 封装返回结果
        List<NearUserDTO> list = new ArrayList<>();
        for (GeoResult<GeoLocation<String>> result : results) {
            NearUserDTO dto = new NearUserDTO();
            dto.setUserId(Long.valueOf(result.getContent().getName()));
            dto.setDistance(result.getDistance().getValue()); // 距离(米)
            list.add(dto);
        }
        return list;
    }
}
4. 返回实体
代码语言:java
复制
@Data
public class NearUserDTO {
    private Long userId;
    private Double distance; // 距离多少米
    // 可扩展:昵称、头像、性别、年龄等
}
5. 位置更新(GEOADD)—— 附带 LUA 原子保活
代码语言:java
复制
// 用户上报位置,同时刷新在线心跳
public void updateLocation(String userId, double lng, double lat) {
    String geoKey = resolveGeoKey(lng, lat);  // 按城市分片,如 "{beijing}:nearby:users"
    String onlineKey = "online:heartbeat";
    long now = System.currentTimeMillis();
    
    // 使用 Lettuce 异步 + Pipeline 减少 RTT
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        byte[] geoKeyBytes = geoKey.getBytes();
        byte[] member = userId.getBytes();
        // GEOADD key longitude latitude member
        connection.geoCommands().geoAdd(geoKeyBytes, new Point(lng, lat), member);
        // 同时更新在线心跳 ZSET,score = 当前时间戳
        connection.zSetCommands().zAdd(onlineKey.getBytes(), now, member);
        return null;
    });
}

亮点: Pipeline 合并操作,一次网络往返完成 Geo 写入和心跳保活;resolveGeoKey 按城市分片,避免大 key,同时保证同一个城市的附近查询都在同一 Redis 节点。

6. 附近的人搜索(GEORADIUS)—— 批量缓存穿透保护
代码语言:java
复制
public List<NearbyUser> searchNearby(double lng, double lat, double radiusKm, int page, int size) {
    String geoKey = resolveGeoKey(lng, lat);
    // Redis Geo 不支持游标分页,这里用 COUNT 加客户端 offset 模拟
    // 注意:大 offset 性能差,业务限制最大页数
    int maxPage = 10;
    if (page > maxPage) return Collections.emptyList();
    
    RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs()
            .includeDistance()
            .sortAscending()
            .limit(size * maxPage);  // 多取一些,后面根据 offset 截断
    
    // GEORADIUS key longitude latitude radius km WITHDIST ASC
    GeoResults<RedisGeoCommands.GeoLocation<String>> results =
            redisTemplate.opsForGeo().radius(geoKey,
                    new Circle(new Point(lng, lat), new Distance(radiusKm, Metrics.KILOMETERS)),
                    args);
    
    if (results == null) return Collections.emptyList();
    
    // 截断分页
    List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();
    int start = (page - 1) * size;
    int end = Math.min(start + size, content.size());
    if (start >= content.size()) return Collections.emptyList();
    
    List<String> userIds = content.subList(start, end).stream()
            .map(g -> g.getContent().getName())
            .collect(Collectors.toList());
    
    // 批量加载用户信息:先查 Redis 缓存,缺失的批量查 DB,再回填缓存
    return batchLoadUserInfo(userIds);
}

亮点: 使用 limit(size * maxPage) 一次性取出足够多的结果,避免多次 GEORADIUS,减轻 Redis 压力;分页纯内存截断,同时限制最大翻页深度,防止滥用。

7. 批量加载用户信息—— 防缓存穿透布隆过滤
代码语言:java
复制
private List<NearbyUser> batchLoadUserInfo(List<String> userIds) {
    // 1. 先批量从 Redis 缓存获取
    List<String> cacheKeys = userIds.stream().map(id -> "user:info:" + id).collect(Collectors.toList());
    List<Object> cachedData = redisTemplate.opsForValue().multiGet(cacheKeys);
    
    // 2. 找出缓存未命中的 ID,并过滤掉布隆过滤器认为不存在的非法 ID
    List<String> missedIds = new ArrayList<>();
    for (int i = 0; i < userIds.size(); i++) {
        if (cachedData.get(i) == null) {
            String uid = userIds.get(i);
            if (bloomFilter.mightContain(uid)) {  // 可能存在的才去查 DB
                missedIds.add(uid);
            }
        }
    }
    
    // 3. 批量查 DB(使用 IN 查询)
    Map<String, User> dbResult = Collections.emptyMap();
    if (!missedIds.isEmpty()) {
        List<User> users = userMapper.selectBatchIds(missedIds);
        dbResult = users.stream().collect(Collectors.toMap(User::getId, u -> u));
        // 回填缓存(异步批量写入)
        cacheService.asyncBatchPut(dbResult);
    }
    
    // 4. 组装结果,保持原有排序
    return userIds.stream().map(id -> {
        Object cached = cachedData.get(userIds.indexOf(id));
        if (cached != null) {
            return buildNearbyUser((User)cached);
        }
        User u = dbResult.get(id);
        if (u != null) return buildNearbyUser(u);
        // 非法用户标记,布隆过滤器也加上(这里忽略详细)
        return null;
    }).filter(Objects::nonNull).collect(Collectors.toList());
}

亮点: 布隆过滤器前置防御,拒绝大量恶意或已注销的用户 ID 打到 DB;缓存回填异步化,不阻塞本次请求。

8. 过期用户清理—— 基于 LUA 的原子扫描与移除
代码语言:java
复制
// 定时任务,每分钟执行一次,清除心跳超过 3 分钟的用户
private String CLEAN_SCRIPT = 
    "local members = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1]) " +
    "if #members > 0 then " +
    "   redis.call('ZREM', KEYS[1], unpack(members)) " +
    "   for i, member in ipairs(members) do " +
    "       local geoKey = redis.call('GET', 'user:geo:key:' .. member) " +
    "       if geoKey then redis.call('ZREM', geoKey, member) end " +
    "   end " +
    "   return #members " +
    "else return 0 end";

public void cleanOfflineUsers() {
    long expireTimestamp = System.currentTimeMillis() - 180_000; // 3分钟前
    // 在 Redis Cluster 中需要针对每个分片执行,这里简略
    Long removed = redisTemplate.execute(
            new DefaultRedisScript<>(CLEAN_SCRIPT, Long.class),
            Collections.singletonList("online:heartbeat"),
            String.valueOf(expireTimestamp));
    log.info("Cleaned {} offline users", removed);
}

亮点: 使用 LUA 脚本保证“查找过期 - 移除在线记录 - 移除对应 Geo Key 中的成员”这一系列操作原子化,避免并发导致幽灵数据;另外维护一个 user:geo:key:userId 记录用户当前落在哪个城市分片,便于清理时精准删除。

技术亮点(面试主动说,直接加分)⭐

  1. 直接使用 Redis 原生 Geo 指令,性能比自己实现 GeoHash 快 10 倍以上
  2. includeDistance() 自动计算距离,无需业务代码二次计算
  3. limit 限制返回条数,防止 Redis 压力过大
  4. 异步上报位置(可扩展),不阻塞用户请求
  5. 无锁设计,Redis 单线程天然高并发
  6. 内存级查询,QPS 轻松 10w+

本场景技术难点 + 解决方案(面试必问)🧠

我把设计过程中最典型的几个坑和应对策略整理成表格,一目了然:

难点

风险

解决方案

大 key 热点

单城市 Geo Key 用户超百万,高并发下 Redis 单分片 CPU 打满

按城市+网格二级分片,如 {beijing:g1} 2. 查询时计算相邻网格并合并结果 3. 多副本读写分离

Geohash 边界跨越

用户在分片边缘,附近的人落在另一个分片里

查询时根据位置动态计算可能涉及的周围 8 个网格,并发查多个 key 并归并排序

海量并发的位置写入

千万用户同时移动上报,Redis 写压力巨大

客户端运动阈值过滤(>50米 / >2分钟) 2. 异步写入:网关层收报后先入 MQ,消费端批量写 Redis 3. Redis Cluster 原生横向扩容

分页深度过大

GEORADIUS 无游标,大 offset 严重浪费 Redis 计算资源

业务限制最大翻页数(如 10 页) 2. 交互上改为“实时刷新”替代“跳页” 3. 缓存用户一段时间内“看过的人”并去重,减少重复查询

缓存穿透/击穿

附近的人结果中包含大量无效用户 ID,高并发穿透到 DB

布隆过滤器维护合法在线用户集 2. 批量加载用户信息,空值也缓存短期 3. 用户注销时同步删除缓存和布隆位

Redis Geo 无法直接设置 TTL

离线用户残留,导致 Geo 集合不断膨胀

在线心跳 ZSET + 定时任务 LUA 清理 2. 额外记录用户→Geo Key 映射,保证清除时知道去哪个分片删 3. 兜底:每天低峰期全量重建活跃用户 Geo 数据

距离排序的精度和性能取舍

球面距离计算昂贵,尤其高并发大量结果排序

Redis Geo 内部用简化球面公式,精度足够(相对误差<0.5%)。如果业务非要超高精度,可在应用层用 Haversine 再算一遍,但只对最终返回的小结果集做。

我给你整理成面试官最爱听的结构化答案,不啰嗦、全是踩坑点。

1. 难点:海量用户下查询性能差

解决方案

  • 使用 Redis Geo 做内存查询,不直接查数据库
  • 城市 / 区域做 Redis 分片,避免单节点压力
  • 限制查询半径(如最多 5km、10km),不允许全量扫
2. 难点:用户位置频繁变动,频繁写导致性能抖动

解决方案

  • 客户端节流上报:静止时不上报、移动超过一定距离再上报
  • 采用异步队列(Kafka/RocketMQ) 削峰写流量
  • Redis 写性能远高于 MySQL,位置只写 Redis,定时异步落库
3. 难点:GeoHash 边界问题(相邻区域查不到)

解决方案

  • Redis Geo 底层自动解决边界问题,无需手动处理
  • 若自研:查询当前格子 + 周围 8 个格子
4. 难点:用户隐私泄露风险

解决方案

  • 提供开关:关闭后不展示、不存储位置
  • 不存储精确经纬度,只存模糊位置(如 100 米精度)
  • 位置数据定时过期,不永久保存
  • 敏感位置(医院、部队)做位置偏移
5. 难点:高并发下缓存击穿、雪崩

解决方案

  • Redis 集群 + 主从 + 哨兵
  • 不使用批量 KEY,避免大 KEY 产生
  • 接口限流:单用户每秒最多查 2 次
6. 难点:需要筛选(性别、年龄、在线状态)

解决方案

  • 先通过 Redis Geo 查出附近用户 ID
  • 再到用户库 / ES / 缓存中批量过滤
  • 架构思路:空间检索 + 属性过滤 分离

一个“面试亮点”: 动态网格切分设计

如果允许我多花一分钟展示一下思考深度,我提一个动态网格方案,应对极端热点事件(比如演唱会、跨年活动)导致局部人群密度爆炸的情况:

网格基于 Geohash 前缀长度动态调整,人群密度越高,Geohash 前缀越长(格子越小)。这样就能自适应调节单 key 大小,避免写热点,同时查询时只需扩大 Geohash 前缀搜索范围。这块如果面试官感兴趣可以再展开。

极简总结(面试收尾一句话)📌

附近的人核心是 Redis Geo 做高性能空间检索,通过客户端节流、异步削峰、Redis 集群、隐私模糊化解决高并发、性能、隐私三大难题,可支撑百万 DAU 级别的互联网应用。

【别走,交个朋友】

我是Rain ,一个喜欢把复杂技术讲透、让代码落地的实践者。

如果你厌倦了四处收集碎片化的八股文,想看看一线项目里真实的架构决策和踩坑记录,欢迎来我的公众号「Rain的Java大神之路」坐坐。

在这里,我会把每一次技术复盘、每一个项目的设计源码,毫无保留地分享给你。

🔥技术这条路很酷,我们结伴同行。

🚀Keep Coding, Keep Loving —— 与所有 Java 同路人共勉。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 附近的人功能如何设计
    • 🧭 问题理解(先对齐需求)
    • 核心需求拆解 📋
    • 整体架构设计 🏗️
      • 核心流程:
    • 核心技术方案 ⚙️
      • 1. 地理位置索引:GeoHash 算法 📍
      • 2. 数据存储方案 💾
      • 为什么首选 Redis Geo
      • 3. 高性能查询优化 🚀
      • 🧠4. 核心实现:Redis 命令与编码细节
      • ① 用户位置更新
      • ② 搜索附近的人
      • ③ 扩展性考虑
      • 📊 整体架构(带图更直观)
    • 进阶考虑与优化 🎯
      • 1. 隐私保护 🔒
      • 2. 高并发处理 ⚡
      • 3. 容灾与扩展 🛡️
      • 4. 🔹分页的坑
      • 5. 🔹热点数据
      • 6. 🔹用户频繁移动
      • 7. 🔹踢除离线用户
      • 8. 🧩一些深层追问的思考
      • Q: 如果不满足 Redis Geo,比如要在附近人中按兴趣过滤?
      • Q: 数据倾斜怎么处理?
      • Q: 如何保证服务的最终一致性?
    • 总结 ✨
  • 附近的人功能:核心代码 + 技术难点全解 🔥
    • 核心代码(Java + Redis Geo)✨
      • 1. 依赖(Spring Boot 环境)
      • 2. 核心配置(Redis 连接工厂)
      • 3. 核心业务代码(面试必背)
      • 4. 返回实体
      • 5. 位置更新(GEOADD)—— 附带 LUA 原子保活
      • 6. 附近的人搜索(GEORADIUS)—— 批量缓存穿透保护
      • 7. 批量加载用户信息—— 防缓存穿透布隆过滤
      • 8. 过期用户清理—— 基于 LUA 的原子扫描与移除
    • 技术亮点(面试主动说,直接加分)⭐
    • 本场景技术难点 + 解决方案(面试必问)🧠
      • 1. 难点:海量用户下查询性能差
      • 2. 难点:用户位置频繁变动,频繁写导致性能抖动
      • 3. 难点:GeoHash 边界问题(相邻区域查不到)
      • 4. 难点:用户隐私泄露风险
      • 5. 难点:高并发下缓存击穿、雪崩
      • 6. 难点:需要筛选(性别、年龄、在线状态)
    • 一个“面试亮点”: 动态网格切分设计
    • 极简总结(面试收尾一句话)📌
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档