1. 程式人生 > 其它 >關於mysql做距離篩選的兩種方法

關於mysql做距離篩選的兩種方法

使用mysql自帶的函式計算距離作為篩選條件

  • 這種方式是網上比較常見的,缺點很明顯,不能使用索引,查詢非常的慢,幾萬條資料量查詢都慢的要死
/**
*  @param :lat 緯度
*  @param :lon 經度
*  @param :dis 距離範圍
**/
SELECT a.*,ROUND(6378.138 * 2 * ASIN(SQRT(POW(SIN((:lat * PI() / 180 - a.latitude * PI() / 180) / 2), 2) + COS(:lat * PI() / 180) * COS(a.latitude * PI() / 180) * POW(SIN((:lon * PI() / 180 - a.longitude * PI() / 180) / 2), 2))) * 1000) AS distance FROM maoyan_cinema a WHERE a.del_flag = 0 HAVING distance < :dis ORDER BY distance ASC

使用redis計算距離

  • 先將資料庫裡的資料全部匯入到redis(經緯度和主鍵)即可
//匯入到redis部分程式碼
public void syncGeo() {
        //查詢經度緯度主鍵id儲存在list
        List<Object[]> list = xxx.getStationPoint();
        if (CollectionUtils.isEmpty(list) || list.size() < 1) {
            return;
        }
        Map<Long, Point> map = new HashMap<>(hashMapInitialCapacity);
        for (Object[] o : list) {
            map.put(Long.parseLong(o[0].toString()), new Point(Double.parseDouble(o[2].toString()), Double.parseDouble(o[1].toString())));
        }
        GeoOperations geoOperations = redisTemplate.opsForGeo();
        Long stations = geoOperations.geoAdd("redisKeyGeo", map);
        redisTemplate.expire("redisKeyGeo", 2, TimeUnit.DAYS);
    }
  • 儲存完後在redis視覺化工具可以看到

  • 也可以使用命令列檢視其中的某條記錄

  • geo計算滿足距離範圍內主鍵id列表

           Circle circle = new Circle(new Point("入參經度", "入參緯度"), new Distance("距離範圍", Metrics.KILOMETERS));
            //這裡限制1w條,可根據實際業務確定需要多少條資料
            RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending().limit(10000);
            GeoResults<RedisGeoCommands.GeoLocation<Integer>> results = geoOperations.geoRadius("redisKeyGeo", circle, args);
            Map<Long, Double> stationIdsAndDistance = new LinkedHashMap<>();
            List<GeoResult<RedisGeoCommands.GeoLocation<Integer>>> content = results.getContent();
  • 得到範圍內的所有主鍵id,再使用mysql進行各種條件篩選。這種方式可以用到索引,測試geo計算距離也很快,只需要幾毫秒