黑马点评-附近商户

附近商户,附近地图都是基于地理坐标实现的

redis中实现附近商户的功能是基于GEO数据结构

GEO数据结构

geo代表地理坐标,允许存储地理坐标信息,帮助根据经纬度来检索数据

相关命令

GEOHASH:是将坐标以01进制的形式进行编码,在返回

GEOSEARCHSOTRE:以sortedset返回搜索的结果

练习

1.输入三个地址的坐标,先输入经纬度,后输入经纬度坐标名称

可以看见,redis中以ZET存储,也就是sortedset集合

2.可以指定输出的距离,可以看见bjn到bjx的距离是111.2264千米

3.搜索排序可以用GEORADIUS或者GEOSEARCH

下面的命令表示按照天安门的经纬度坐标(116.39 39.411)在g1中按照半径200km的范围搜索坐标,并附上距离。

其中,还可以按照矩形的范围搜索,以及按照升序降序的功能,还能实现搜索最大数量内的坐标

附近商户搜索

分析一下流程,首先点击某个频道,可以看见频道下的对应商户,这是GET请求类型,请求接口是,shop/of/type?&typeId=1,1就是对应的美食频道,申请的请求中带有请求分页查询和用户的当前位置坐标

实现思路

由于要计算距离,而商户的坐标存储在数据库中,计算后在返回就会大量的消耗计算资源,所以先将数据库中的商户信息的id和坐标存储在redis中的geo数据结构中,然后计算后在返回对应商户id,去数据库中查询店铺信息,这样提升了并发性。

首先先将店铺的坐标和id按照typeId分组存储在redis中

    @Resource
    private ShopServiceImpl shopService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Test
    void loadShopData() {
        //1.查询店铺信息
        List<Shop> list = shopService.list();
        //2.将店铺按照typeId进行分组,typeId一致的放到一个集合中,Long存入的是typeId,list存入相应的店铺集合
        //可以利用for循环一个一个遍历存入map类型中,但可以直接利用stream流,按照typeId进行分组
        Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));
        //3.分组写入redis中
        for(Map.Entry<Long,List<Shop>> entry:map.entrySet()) {
            //3.1获取类型id
            Long typeId = entry.getKey();
            String key = "shop:geo:" + typeId;
            //3.2获取同类型店铺的集合
            List<Shop> value = entry.getValue();
            //3.3写入redis GEOADD key x y member
            for (Shop shop : value) {
                stringRedisTemplate.opsForGeo().add(key, new Point(shop.getX(), shop.getX()), shop.getId().toString());

            }
        }
    }

redis对应的存储信息

### 商户类型的缓存机制 为了实现商户类型的高效缓存管理,可以采用多种技术手段来确保数据的一致性和性能优化。具体来说: #### 使用 Redis 实现缓存存储 Redis 是一种高性能的键值对数据库,非常适合用于缓存场景。对于商户类型的数据,可以通过设置合理的 TTL (Time To Live) 来控制缓存的有效期。 ```java // 设置带有随机TTL的缓存条目 String key = "merchantType:" + merchantId; string value = getMerchantTypeFromDB(merchantId); int ttlInSeconds = baseTtl + random.nextInt(variationRange); // 添加随机偏移量防止雪崩效应 jedis.setex(key, ttlInSeconds, value); ``` 此代码片段展示了如何通过给定的基础存活时间和一定范围内的随机数相结合的方式设定缓存项的生命期限[^3]。 #### 处理高一致性的业务需求 针对需要较高一致性的查询操作(如店铺详情),应采取主动更新策略,在每次修改源数据之后立即同步刷新对应的缓存记录;同时保留基于时间戳或版本号的过期机制作为备用措施以防万一[^2]。 当接收到新的订单请求或其他可能影响到特定商家信息变更的通知时,则应当及时触发相应的事件处理器来进行实时同步工作: ```java public void handleOrderEvent(Order order){ String account = order.getAccount(); // 更新数据库中的商户信息... updateDatabaseWithNewData(account); // 同步清除并重建关联账户下的所有缓存副本 invalidateAndRebuildCacheForAccount(account); } ``` 上述伪码描述了一个典型的事物驱动型架构下保持分布式环境中多个节点间共享状态一致的方法之一[^1]。 #### 防护极端情况的发生 为了避免突发流量冲击造成系统崩溃的风险,建议引入熔断器模式以及速率限制算法等防护组件。这些工具可以帮助识别异常行为并在必要时候自动降低服务质量等级以保护核心功能不受损害。 另外值得注意的是,在某些特殊情况下可能会遇到所谓的“缓存穿透”问题——即恶意攻击者故意构造不存在于任何地方的对象ID试图耗尽服务器资源。对此类威胁最有效的防御办法就是预先加载一部分热点数据至本地内存中形成一层额外的安全屏障[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值