1. Введение
Начальник вдруг хочет запустить запрос на получение точки бизнес-агента в радиусе одного километра от текущего местоположения. Онлайн завтра! Когда я получил эту просьбу, меня чуть не вырвало кровью, а время было слишком напряженным. Спешите ознакомиться с соответствующей технической подборкой. После долгих метаний я, наконец, выполнил это требование в десять часов вечера. Теперь подытожим общую идею реализации.
2. MySQL не подходит
Сталкиваясь со спросом, первое, о чем нужно подумать, это то, можно ли удовлетворить существующие вещи и какова стоимость.
MySQLэто первое, о чем я могу подумать, в конце концов, большая часть данных будет сохранена вMySQL. но использоватьMySQLнужно рассчитатьGeohash. Нужно использовать много математических и геометрических вычислений, а также нужно изучать знания, связанные с географией, порог высок, невозможно выполнить требования за короткое время, а в долгосрочной перспективе это не так.MySQLобласти знаний, поэтому я не думал об этом.
GeohashСсылаться наблог woo woo woo.cn на.com/LBSwhile/afraid/331…
2. ГЕО в Redis
Redisтот, с которым мы больше всего знакомыK-VБаза данных, которая часто используется в качестве высокопроизводительной базы данных кэша и используется в большинстве проектов. от3.2версия началась она начала предоставлятьGEOВозможности для реализации функций, зависящих от информации о географическом местоположении, таких как близлежащие местоположения, расчет расстояний и т. д.GEOСоответствующие команды следующие:
Команды Redis | описывать |
---|---|
GEOHASH | Возвращает один или несколько элементов позицииGeohashвыражать |
GEOPOS | Возвращает местоположение (долготу и широту) всех заданных элементов местоположения из ключа |
GEODIST | Возвращает расстояние между двумя заданными местоположениями |
GEORADIUS | Найдите элементы в пределах определенного радиуса с заданной широтой и долготой в качестве центра |
GEOADD | Добавить указанное геопространственное местоположение (широта, долгота, имя) к указанному ключу |
GEORADIUSBYMEMBER | Найдите элемент, расположенный в указанном диапазоне, центральная точка определяется заданным элементом позиции |
Redis будет предполагать, что Земля представляет собой идеальную сферу, поэтому могут быть некоторые отклонения в расчетах местоположения, которые, как говорят, составляют
2.1 Напишите географическую информацию
Так как же получить все элементы в пределах целевого единичного радиуса? Мы можем передать широту и долготу всех местоположений черезGEOADD
Преобразуйте эту географическую информацию в 52-битнуюGeohashнаписатьRedis.
Формат команды:
geoadd key longitude latitude member [longitude latitude member ...]
Соответствующий пример:
redis> geoadd cities:locs 117.12 39.08 tianjin 114.29 38.02 shijiazhuang
(integer) 2
означает, что долгота117.12
Широта39.08
Местоположениеtianjin
и долгота как114.29
Широта38.02
Местоположениеshijiazhuang
Присоединяйсяkeyзаcities:locs
изsorted setв коллекции. Можно добавить одно или несколько мест. Затем мы можем использовать другие команды для вычисления географического положения.
Допустимые долготы от -180 градусов до 180 градусов. Допустимые значения широты: от -85,05112878 градусов до 85,05112878 градусов. Команда вернет ошибку, когда положение координат превысит указанный выше диапазон.
2.2 Районы в радиусе статистической единицы
мы можем использоватьGEORADIUS
найти все элементы в пределах определенного радиуса с заданной широтой и долготой.
Формат команды:
georadius key longtitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
Это отношение порядкаGEOADD
Чтобы быть немного сложнее:
-
radiusДлина радиуса, обязательна. Назад
m
,km
,ft
,mi
, — это параметр единицы длины, выберите один из четырех. - WITHCOORDВозвращает долготу и широту элемента местоположения, не требуется.
- WITHDISTПри возврате элемента position также возвращается расстояние между элементом position и центральной точкой. Единица расстояния такая же, как единица запроса, которая не требуется.
- WITHHASHВозвращает 52-битную точность позицииGeohashзначение, необязательно. Я все равно редко использую это, возможно, некоторые другие имеют тенденцию быть низкоуровневыми.LBSСлужбе приложений это необходимо.
- COUNTВозвращает количество подходящих элементов позиции, необязательно. Например, верните первые 10, чтобы избежать проблем с производительностью из-за слишком большого количества совпадающих результатов.
- ASC|DESCСортировать по, необязательно. По умолчанию возвращает несортированный, но в основном нам нужно отсортировать. Обратитесь к центральному положению, используйте от ближнего к дальнемуASC, используется от дальнего к ближнемуDESC.
Например, мыcities:locs
Найдите в (115.03, 38.44) центр радиуса200km
, результат содержит название города, соответствующие координаты и расстояние (км) от центральной точки, расположенное от ближайшего к самому дальнему. Команда выглядит следующим образом:
redis> georadius cities:locs 115.03 38.44 200 km WITHCOORD WITHDIST ASC
1) 1) "shijiazhuang"
2) "79.7653"
3) 1) "114.29000169038772583"
2) "38.01999994251037407"
2) 1) "tianjin"
2) "186.6937"
3) 1) "117.02000230550765991"
2) "39.0800000535766543"
можете добавить
COUNT 1
чтобы найти ближайшее место.
3. На основе реального боя Redis GEO
После того, как общая принципиальная идея закончена, следующим шагом является практическая работа. комбинироватьSpring BootПрименение Как мы должны это сделать?
3.1 Среда разработки
должен иметьGEOхарактеристикаRedisверсия, здесь я используюRedis 4. Кроме того, наш клиент используетspring-boot-starter-data-redis
. Здесь мы будем использоватьRedisTemplate
объект.
3.2 Пакетное добавление информации о местоположении
В качестве первого шага нам нужно инициализировать данные о местоположении, чтобыRedisсередина. существуетSpring Data Redisкоордината положения(lng,lat)
можно упаковать вorg.springframework.data.geo.Point
в объекте.然后指定一个名称,就组成了一个位置GeoИнформация.RedisTemplate
Предоставляет метод массового добавления информации о местоположении. мы можем поставитьРаздел 2.1Команда добавления преобразуется в следующий код:
Map<String, Point> points = new HashMap<>();
points.put("tianjin", new Point(117.12, 39.08));
points.put("shijiazhuang", new Point(114.29, 38.02));
// RedisTemplate 批量添加 Geo
redisTemplate.boundGeoOps("cities:locs").add(points);
можно комбинироватьИнтерфейс ApplicationRunner, предоставляемый Spring Bootреализовать инициализацию.
@Bean
public ApplicationRunner cacheActiveAppRunner(RedisTemplate<String, String> redisTemplate) {
return args -> {
final String GEO_KEY = "cities:locs";
// 清理缓存
redisTemplate.delete(GEO_KEY);
Map<String, Point> points = new HashMap<>();
points.put("tianjin", new Point(117.12, 39.08));
points.put("shijiazhuang", new Point(114.29, 38.02));
// RedisTemplate 批量添加 GeoLocation
BoundGeoOperations<String, String> geoOps = redisTemplate.boundGeoOps(GEO_KEY);
geoOps.add(points);
};
}
Географические данные сохраняются в MySQL, а затем синхронизируются с Redis.
3.3 Запрос определенного местоположения поблизости
RedisTemplate
противGEORADIUS
У команд также есть обертки:
GeoResults<GeoLocation<M>> radius(K key, Circle within, GeoRadiusCommandArgs args)
Circle
Объект — это площадь, охватываемая пакетом (рис. 1), а искомые элементы — координаты центральной точки.Point
Объект, радиус, метрика, например:
Point point = new Point(115.03, 38.44);
Metric metric = RedisGeoCommands.DistanceUnit.KILOMETERS;
Distance distance = new Distance(200, metric);
Circle circle = new Circle(point, distance);
GeoRadiusCommandArgs
используется для инкапсуляцииGEORADIUS
некоторые необязательные параметры команды см.Раздел 2.2серединаWITHCOORD
,COUNT
,ASC
И т. д., например, нам нужно включить в возвращаемый результат координаты, центральное расстояние и первые 5 фрагментов данных, отсортированных от ближнего к дальнему:
RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands
.GeoRadiusCommandArgs
.newGeoRadiusArgs()
.includeDistance()
.includeCoordinates()
.sortAscending()
.limit(limit);
затем выполнитьradius
метод получитGeoResults<RedisGeoCommands.GeoLocation<String>>
В результате инкапсуляции мы можем получить нужные нам данные, проанализировав этот итерируемый объект:
GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo()
.radius(GEO_STAGE, circle, args);
if (radius != null) {
List<StageDTO> stageDTOS = new ArrayList<>();
radius.forEach(geoLocationGeoResult -> {
RedisGeoCommands.GeoLocation<String> content = geoLocationGeoResult.getContent();
//member 名称 如 tianjin
String name = content.getName();
// 对应的经纬度坐标
Point pos = content.getPoint();
// 距离中心点的距离
Distance dis = geoLocationGeoResult.getDistance();
});
}
3.4 Удаление элементов
Иногда нам может понадобиться удалить элемент position, ноRedisизGeoНет команды для удаления участников. Однако, поскольку в его основеzset
, мы можем использоватьzrem
команда на удаление, соответствующаяJavaКод:
redisTemplate.boundZSetOps(GEO_STAGE).remove("tianjin");
4. Резюме
Сегодня мы используемRedisизGeoЭта функция реализует общие требования к географическим информационным запросам поблизости и проста в использовании. На самом деле используйте другойNosqlбаза данныхMongoDBтакже может быть достигнуто. В случае небольшого количества данныхRedisЭто было в состоянии удовлетворить потребности хорошо. Можно использовать, если объем данных большойMongoDBреализовать. в текстеDEMOСледовать:Код Фермер Маленький Толстый Брат, ответ на общедоступный номерredisgeoПолучать.
关注公众号:Felordcn获取更多资讯