ST_Distance는 공간 쿼리에 인덱스를 사용하지 않습니다


10

PostgreSQL 9.3.5에서 PostGIS 2.1을 실행하여 가장 간단한 쿼리에도 공간 인덱스를 사용할 수는 없습니다. 전체 데이터 세트는 (여기에서 인구 카운트 그리드) 8 백만 점이다 . 테이블은 다음과 같이 생성됩니다

CREATE TABLE points (
    population DOUBLE PRECISION NOT NULL,
    location GEOGRAPHY(4326, POINT) NOT NULL
)
CREATE INDEX points_gix ON points USING GIST(location);

쿼리는 얻는 것만 큼 간단합니다

SELECT SUM(population)
FROM points
WHERE ST_Distance(
    location,
    ST_GeographyFromText('SRID=4326; POINT(0 0)')
) < 1000

PostgreSQL은 항상 Seq 스캔을 사용합니다. 나는 10000 포인트의 서브 세트를 시도했지만 여전히 Seq 스캔입니다. 어떤 아이디어?


3
색인을 사용할 수있는 기능을 사용하지 않습니다. 대신 st_dwithin을 사용하십시오. 그런 다음 기능은 먼저 인덱스 스캔을 수행합니다.
Nicklas Avén

쿼리가 수행하는 작업 (테이블의 각 포인트에서 고정 포인트까지의 거리 계산)을 생각하면 인덱스를 사용할 수 없는 이유를 이해할 있습니다. 대신 ST_DWithin 같은 인덱스를 사용할 수있는 연산자 사용
빈스

답변:


19

ST_Distance는 실제로 모든 점 쌍 사이의 거리를 계산하므로 인덱스를 사용할 수 없습니다. 따라서 쿼리는 시퀀스 스캔을 수행 한 다음 지정한 거리보다 작은 지오메트리를 선택합니다. 인덱스를 사용하는 ST_DWithin을 찾고 있습니다.

SELECT SUM(population) FROM points 
WHERE ST_DWithin(location, ST_GeographyFromText('SRID=4326; POINT(0 0)'), 1000);

ST_Distance는 종종 ORDER BY 및 / 또는 LIMIT와 함께 인덱스를 사용하는 쿼리로 얻은 결과를 정렬하는 데 더 유용합니다.


1
감사. 질문을하기 전에 문서를 읽어야합니다.
synapse

1
와! 감사합니다! st_distance를 st_dwithin으로 변경하여 느린 쿼리를 100x 폴드 이상으로 "가속화"했습니다. (내가 더 조심했다면 처음부터 이런 일이 발생하지 않았기 때문에 "가속화"라고 말함)
Hendy Irawan

1
트윗 담아 가기 아니에요. 만들기 쉬운 실수입니다.
존 파월

@ JohnPowellakaBarça 다른 최적화를 추가했지만 ( 매우 손실이 있지만 내 경우에 대한 답변을 추가했습니다) 올바른 방향으로 안내했습니다. 감사합니다.
Hendy Irawan

4

@ JohnPowellakaBarça가 말했듯 ST_DWithin()정확성 을 원할 때 갈 길 입니다.

그러나 제 경우에는 대략적인 추정 만 원 하므로 ST_DWithin()내 요구에 비해 너무 비쌌습니다 (쿼리 비용). 내가 사용 &&하고 ST_Expand(box2d)합니다 (함께이 실수를하지 않는 geometry대신 버전). 예:

SELECT * FROM profile
  WHERE
    address_point IS NOT NULL AND
    address_point && CAST(ST_Expand(CAST(ST_GeomFromText(:point) AS box2d), 0.5) AS geometry;

당장 눈에 띄는 것은 미터 대신 각도를 다루고, 회전 타원체에서 원 대신 경계 상자를 사용한다는 것입니다. 내 사용 사례를 들어,에서 다운이 삭감 (24) MS 단지에 2 밀리 초 (로컬 SSD에서). 그러나 동시 연결 및 거의 IOPS 할당량 (100 IOPS)이 거의없는 AWS RDS PostgreSQL의 프로덕션 데이터베이스의 경우 원래 ST_DWithin()쿼리는 너무 많은 IOPS를 소비 하고 IOPS 할당량이 고갈되면 2000ms 이상 을 훨씬 더 많이 실행할 수 있습니다 .

이것은 모든 사람을위한 것이 아니라 속도의 정확도를 희생하거나 IOPS를 절약 할 수있는 경우이 방법이 적합 할 수 있습니다. 아래 쿼리 계획에서 볼 수 있듯이 상자 다시 표시에는 필터가 필요하지 않으며 다시 검사 조건 만 사용하는 ST_DWithin반면 비트 맵 힙 스캔 내부에는 공간 필터 &&가 필요합니다.

또한 IS NOT NULL문제가 없으면 쿼리 계획이 더 나빠질 수 있습니다. GIST 지수가 "충분히 똑똑하지"않은 것 같습니다. (물론 열이 NOT NULL인 경우 필요하지 않습니다 . 제 경우에는 NULL가능합니다)

ST_DWithin(geography, geography, 100000, FALSE)300 IOPS의 AWS RDS 512MB RAM에서 20000 개의 행 테이블 :

Aggregate  (cost=4.61..4.62 rows=1 width=8) (actual time=2011.358..2011.358 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..4.61 rows=1 width=0) (actual time=1735.025..2010.635 rows=1974 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)))
        Filter: (((status)::text = 'ACTIVE'::text) AND ((gender)::text = 'MALE'::text) AND (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(address_point, '100000'::double precision)) AND _st_dwithin(address_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, false)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(hometown_point, '100000'::double precision)) AND _st_dwithin(hometown_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, false))))
        Rows Removed by Filter: 3323
        Heap Blocks: exact=7014
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=1716.425..1716.425 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.42 rows=1 width=0) (actual time=1167.698..1167.698 rows=16086 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.42 rows=1 width=0) (actual time=548.723..548.723 rows=7846 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
Planning time: 47.366 ms
Execution time: 2011.429 ms

20000 행 테이블 &&ST_Expand(box2d)300 IOPS가있는 AWS RDS 512MB RAM :

Aggregate  (cost=3.85..3.86 rows=1 width=8) (actual time=584.346..584.346 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..3.85 rows=1 width=0) (actual time=555.048..584.083 rows=1154 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)))
        Filter: (((status)::text = 'ACTIVE'::text) AND ((gender)::text = 'MALE'::text))
        Rows Removed by Filter: 555
        Heap Blocks: exact=3812
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=553.091..553.091 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.42 rows=1 width=0) (actual time=413.074..413.074 rows=4850 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.42 rows=1 width=0) (actual time=140.014..140.014 rows=3100 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
Planning time: 0.673 ms
Execution time: 584.386 ms

더 간단한 쿼리로 다시 :

ST_DWithin(geography, geography, 100000, FALSE)300 IOPS의 AWS RDS 512MB RAM에서 20000 개의 행 테이블 :

Aggregate  (cost=4.60..4.61 rows=1 width=8) (actual time=36.448..36.448 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..4.60 rows=1 width=0) (actual time=7.694..35.545 rows=2982 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography)))
        Filter: (((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(address_point, '100000'::double precision)) AND _st_dwithin(address_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, true)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography) AND ('0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography && _st_expand(hometown_point, '100000'::double precision)) AND _st_dwithin(hometown_point, '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography, '100000'::double precision, true)))
        Rows Removed by Filter: 2322
        Heap Blocks: exact=2947
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=7.197..7.197 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.41 rows=1 width=0) (actual time=5.265..5.265 rows=5680 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.41 rows=1 width=0) (actual time=1.930..1.930 rows=2743 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0101000020E6100000744694F606E75A40D49AE61DA7A81BC0'::geography))
Planning time: 0.479 ms
Execution time: 36.512 ms

20000 행 테이블 &&ST_Expand(box2d)300 IOPS가있는 AWS RDS 512MB RAM :

Aggregate  (cost=3.84..3.85 rows=1 width=8) (actual time=6.263..6.264 rows=1 loops=1)
  ->  Bitmap Heap Scan on matchprofile  (cost=2.83..3.84 rows=1 width=0) (actual time=4.295..5.864 rows=1711 loops=1)
        Recheck Cond: (((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)) OR ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography)))
        Heap Blocks: exact=1419
        ->  BitmapOr  (cost=2.83..2.83 rows=1 width=0) (actual time=4.122..4.122 rows=0 loops=1)
              ->  Bitmap Index Scan on ik_matchprofile_address_point  (cost=0.00..1.41 rows=1 width=0) (actual time=3.018..3.018 rows=1693 loops=1)
                    Index Cond: ((address_point IS NOT NULL) AND (address_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
              ->  Bitmap Index Scan on ik_matchprofile_hometown_point  (cost=0.00..1.41 rows=1 width=0) (actual time=1.102..1.102 rows=980 loops=1)
                    Index Cond: ((hometown_point IS NOT NULL) AND (hometown_point && '0103000020E61000000100000005000000744694F606C75A40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A819C0744694F606075B40D49AE61DA7A819C0744694F606075B40D49AE61DA7A81DC0744694F606C75A40D49AE61DA7A81DC0'::geography))
Planning time: 0.399 ms
Execution time: 6.306 ms

1
잘 쓰고 재미 있어요.
존 파월
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.