PostgreSQL - 지리적 위치 쿼리를 강화하는 방법

17128 단어
최근에 저는 사용자의 지리적 위치 주변에 있는 사서함을 검색하는 작은 모바일 앱을 만들고 싶었습니다. 다른 앱도 있지만 성능이 그다지 좋지 않았기 때문에 몇 가지 솔루션을 찾아보고 싶었습니다!

우선 data.gouv.fr 웹 사이트에서 데이터 세트를 검색했습니다. https://www.data.gouv.fr/fr/datasets/liste-des-boites-aux-lettres-de-rue-france-metropolitaine-et-dom-avec-heure-limite-de-depot-1/ . 이 데이터 세트에는 140,000개의 항목이 포함되어 있습니다. 물론 그와 가까운 사서함을 표시하려는 사용자는 데이터베이스에서 사용 가능한 모든 사서함을 표시할 필요가 없고 예를 들어 10km와 같이 그와 가까운 사서함만 표시할 필요가 있습니다.

내 프로젝트에서는 SpringBoot API를 사용하지만 오늘 관심을 가질 내용은 아닙니다. 실제 문제는 PostgreSQL 데이터베이스에 저장된 모든 데이터를 검색하는 방법입니다. 이 데이터는 편리한 시간에 가까운 지점에 위치합니다!

목표



내 목표는 기록적인 시간 내에 반경 10km 내에 있는 일련의 지점을 찾는 것입니다!



구조에 PostGIS



PostGIS는 PostgreSQL의 확장으로 지리적 위치 검색을 용이하게 하는 새로운 기능과 데이터 유형을 제공합니다.

서버에 PostGIS를 설치하려면:

~$ sudo apt install postgis


그런 다음 데이터베이스에 연결하고 postgis 확장을 추가하여 PosgresSQL에서 활성화해야 합니다. 그러면 선택한 데이터베이스에서만 활성화됩니다.

psql (13.6 (Ubuntu 13.6-0ubuntu0.21.10.1))
Type "help" for help.

postgres=# \c test;
postgres=# CREATE EXTENSION postgis;


이제 ID, 위도, 경도 및 GPS 지점을 포함하는 테이블을 만들 수 있습니다. SRID 4326 값은 지리적 참조 시스템을 사용하는 지점을 선언하는 데 사용됩니다.

CREATE TABLE public.test (
    id bigserial NOT NULL,
    lat real NOT NULL,
    long real NOT NULL,
    location geography(Point, 4326) NOT NULL
);


그런 다음 다음 쿼리를 사용하여 포인트를 쉽게 추가할 수 있습니다.

INSERT INTO test (lat, long, location)
VALUES (49.548462,1.0779799,ST_SetSRID(ST_MakePoint(49.548462,1.0779799), 4326));


최적화



최적화를 검증하기 위해 140,000행의 위치 정보 데이터를 테이블에 추가했습니다.

어리석게도 st_distancesphere 함수로 반경 10km 이내의 모든 지점을 검색하고 싶었습니다.

다음은 반경 10km 내의 모든 GPS 지점을 검색하기 위해 실행하는 쿼리입니다.

EXPLAIN(ANALYSE , BUFFERS )
SELECT st_distancesphere(t.location::geometry,ST_SetSRID(ST_MakePoint(49.548462,1.0779799), 4326))
FROM test t
WHERE
st_distancesphere(t.location::geometry,ST_SetSRID(ST_MakePoint(49.548462,1.0779799), 4326)) < 10000;


PostgeSQL 쿼리 분석은 인덱스가 사용되지 않았으며 쿼리가 조건과 일치하는 21개의 포인트를 반환하는 데 463ms가 걸린다는 것을 보여줍니다.

Gather  (cost=1000.00..2776968.00 rows=47062 width=8) (actual time=160.851..461.391 rows=115 loops=1)
  Workers Planned: 1
  Workers Launched: 1
  Buffers: shared hit=1759
  ->  Parallel Seq Scan on test t  (cost=0.00..2771261.80 rows=27684 width=8) (actual time=218.887..415.046 rows=58 loops=2)
"        Filter: (st_distance(geography((location)::geometry), '0101000020E6100000A48CB80034C64840F5EC03DA673FF13F'::geography, false) < '10000'::double precision)"
        Rows Removed by Filter: 70535
        Buffers: shared hit=1759
Planning Time: 0.376 ms
JIT:
  Functions: 8
"  Options: Inlining true, Optimization true, Expressions true, Deforming true"
"  Timing: Generation 2.485 ms, Inlining 178.841 ms, Optimization 117.797 ms, Emission 56.983 ms, Total 356.106 ms"
Execution Time: 463.431 ms


따라서 쿼리 속도를 높일 수 있는 인덱스를 만들 것입니다(http://postgis.net/workshops/postgis-intro/indexing.html 참조).

CREATE INDEX test_position_geography_index ON test USING GIST(geography(location));


그러나 색인이 여전히 작동하지 않기 때문에 우리는 불쾌한 놀라움에 처해 있습니다. 빠른 검색 후 특정 PostGIS 기능만 인덱싱을 사용할 수 있다는 것을 알았습니다(여기 문서 참조: http://postgis.net/workshops/postgis-intro/indexing.html#spatially-indexed-functions ).

그래서 두 점이 세 번째 매개변수에 지정된 거리에 있으면 true를 반환하는 ST_DWithin 함수를 사용하기로 했습니다.

다음은 분석에 따른 쿼리입니다.

EXPLAIN(ANALYSE , BUFFERS )
SELECT st_distancesphere(t.location::geometry,ST_SetSRID(ST_MakePoint(49.548462,1.0779799), 4326))
FROM test t
WHERE
   ST_DWithin(t.location, ST_SetSRID(ST_MakePoint(49.548462,1.0779799), 4326)::geography, 10000);



Bitmap Heap Scan on test t  (cost=4.95..2390.34 rows=14 width=8) (actual time=1.212..2.205 rows=115 loops=1)
"  Filter: st_dwithin(location, '0101000020E6100000A48CB80034C64840F5EC03DA673FF13F'::geography, '10000'::double precision, true)"
  Rows Removed by Filter: 200
  Heap Blocks: exact=150
  Buffers: shared hit=188
  ->  Bitmap Index Scan on test_position_geography_index  (cost=0.00..4.95 rows=72 width=0) (actual time=0.288..0.288 rows=315 loops=1)
"        Index Cond: (location && _st_expand('0101000020E6100000A48CB80034C64840F5EC03DA673FF13F'::geography, '10000'::double precision))"
        Buffers: shared hit=26
Planning Time: 0.446 ms
Execution Time: 2.288 ms


test_position_geography_index가 사용되고 2.288ms 🤯에서 21포인트를 복구할 수 있음을 알 수 있습니다. 이득은 엄청나며 실행 시간과 프로세서 사용 시간을 상당히 줄일 수 있습니다.

내 편지함 지리 위치 API에서 이를 통해 Apache ab 성능 테스트 도구( https://httpd.apache.org/docs/2.4/fr/programs/ab.html )로 서버 로드를 시뮬레이션할 때 요청 시간을 10분의 1로 단축하고 서버 부하를 완화할 수 있었습니다.

PostGIS와 함께 즐거운 시간 보내세요. 훌륭한 도구입니다 ;)

좋은 웹페이지 즐겨찾기