В докладе описаны шаги, необходимые для создания сервиса по поиску достопримечательностей на карте. Сервис получает массив GPS координат и на выходе отдает ближайшие POI и расстояние до них.
Тезисы:
Для хороших POI нужно много перекрестных источников
Объединенять источники нужно по расстоянию и названию
Название упростить и добавит Левинштейна
Считать в реалтайме удобнее кеша
Go в 10 раз быстрее Питона
Минимальную рамку нужно расширять для каждого типа POI
Запросы в PostGIS должны быть индексные и простые
Посчитать в расстояние в коде быстрее чем в постгресе
Считать расстояние по теореме Пифагора в 7 раз быстрее чем по Хавершину
Обязательно нужно проводить нагрузочное тестирование
2. Определение
POI (point of interest) — достопримечательность
или другой объект, отмеченный точкой на карте.
3.
4. Задачи
• Показывать расстояние от отеля до
ближайших POI.
• Сортировать выдачу по расстоянию до POI
• Объединить несколько источников данных
• Провести нагрузочное тестирование
5. Источники данных
• OSM - полигон
• Expedia - перевод
• Популярный сервис -
рейтинг
• Hotellook - отобранные
пляжи и подъемники
9. Пути решения
Посчитать все заранее Посчитать во время
поиска
+ нет проблем с
производительностью
+ поиск нужных отелей среди всех,
например пляжных или лыжных
— фоновый процесс обновление
данных, за которым нужно следить
— скорость обновления данных
— триггеры при обновлении
отелей или пой
+ нет кэша
+ быстрое изменение параметров
— квадратичная сложность
— необходимо укладываться в 5
секунд
10. Выбор языка
• Python3
$ time python3 calc.py
real0m2.889s
user 0m2.743s
sys 0m0.055s
• GO
$ time ./go_calc
real0m0.274s
user 0m0.250s
sys 0m0.011s vs
14. PostGIS
Было:
Время: 198,762 мс
select * from poi
where ST_DWithin(geom, ST_MakeEnvelope(37.903013,
55.550331, 37.298765, 55.891429, 4326)::geography, 100000);
Seq Scan on poi (cost=0.00..28525.54 rows=423 width=776)
Filter: (((geom)::geography && '0103000020E610000001000007C073'::geography
&& _st_expand((geom)::geography, '100000'::double precision))
AND _st_dwithin((geom)::geography, '0103000020E6100C64B40'::geography,
'100000'::double precision, true))
15. PostGIS
Стало:
Время: 1,298 мс
select * from poi
where geom && ST_MakeEnvelope(38.803013, 55.390331, 36.298765, 56.891429,
4326) and
category = 'metro_station';
Index Scan using metro_station_gix on poi (cost=0.14..8.16 rows=1 width=776)
Index Cond: (geom && '0103000020E6100000010000000F5DF6B14B40'::geometry)
16. Подсчет расстояния
$ time ./pythagorus
46291.37379901246
real 0m0.404s
user 0m0.367s
sys 0m0.015s
$ time ./haversine
46291.36062403893
real 0m2.731s
user 0m2.624s
sys 0m0.045s
package main
import "math"
func main() {
for i := 0; i < 10000000; i++ {
distance(37.735882, 55.359127, 37.637795,
55.771724)
}
}
func distance(lon1,lat1,lon2,lat2 float64) float64 {
rad_per_deg := math.Pi / 180
rm := 6371.0 * 1000.0
lon1 = lon1 * rad_per_deg
lon2 = lon2 * rad_per_deg
lat1 = lat1 * rad_per_deg
lat2 = lat2 * rad_per_deg
x := (lon2 - lon1) * math.Cos(0.5*(lat2+lat1))
y := lat2 - lat1
return rm * math.Sqrt(x*x+y*y)
}