Làm cách nào để thiết lập đúng các chỉ mục cho các truy vấn khoảng cách PostGIS?


17

Tôi đang xây dựng một ứng dụng được cho là truy vấn và trả về mọi thứ Recordtrong một bảng Xcách xa hàng km PointX. RecordsPointXvị trí của được xác định từ (long/lat)thông tin được cung cấp bởi Google Geocode API.

Tôi mới biết đến PostGIS. Sau khi nghiên cứu nhanh, tôi đã tìm thấy câu hỏi này . Câu trả lời dường như nằm dọc theo dòng:

SELECT *
FROM your_table
WHERE ST_Distance_Sphere(the_geom, ST_MakePoint(your_lon,your_lat)) <= radius_mi * 1609.34

Vấn đề là: Mặc dù tôi chỉ mới bắt đầu tại GIS, nhưng khi tôi nhìn vào truy vấn trên, tôi không thể tưởng tượng làm thế nào điều này có thể sử dụng một chỉ mục. Có 2 chức năng gọi. Tôi tưởng tượng bảng được quét cho mọi Record. Tôi muốn sai :)

Câu hỏi: PostGIS có loại chỉ mục nào có khả năng thực hiện truy vấn trên không? Nếu không, cách tiếp cận được đề xuất sẽ làm những gì tôi cần?


Hãy chắc chắn rằng bạn xây dựng chỉ mục phù hợp, trên một diễn viên cho địa lý và áp dụng một ST_SetSRID()cho ST_MakePointtrước khi truyền vào địa lý trong truy vấn.
Vince

Câu trả lời:


37

Có hai chìa khóa để có được hiệu suất truy vấn trắc địa tốt với các bảng lớn có geometrycác cột sử dụng dữ liệu địa lý WGS 1984 (SRID 4326):

  1. Sử dụng ST_DWithinchức năng tìm kiếm bằng chỉ mục không gian có sẵn và sẽ tìm thấy các tính năng địa lý có khoảng cách Cartesian
  2. Xây dựng một chỉ mục bổ sung trên các diễn viên địa lý, vì vậy ST_DWithincó thể sử dụng nó

Vì vậy, hãy nhìn vào những gì xảy ra trong thế giới thực. Đầu tiên chúng ta cần tạo và điền vào bảng một triệu điểm ngẫu nhiên:

DROP TABLE IF EXISTS example1
;

CREATE TABLE example1 (
    idcol   serial      NOT NULL,
    geomcol geometry        NULL,
    CONSTRAINT  example1_pk PRIMARY KEY (idcol),
    CONSTRAINT  enforce_srid CHECK (st_srid(geomcol) = 4326)
)
with (
    OIDS=FALSE
);

INSERT INTO example1(geomcol)
SELECT  ST_SetSRID(
            ST_MakePoint(
            (random()*360.0) - 180.0,
            (acos(1.0 - 2.0 * random()) * 2.0 - pi()) * 90.0 / pi()),
            4326) as geomcol
FROM  generate_series(1, 1000000) vtab;

CREATE INDEX example1_spx ON example1 USING GIST (geomcol);
-- (took about 22 sec)

Nếu chúng tôi thực hiện truy vấn ST_Distance, chúng tôi sẽ quét toàn bộ bảng dự kiến ​​của bạn:

EXPLAIN ANALYZE VERBOSE
SELECT  count(*)
FROM    example1
WHERE   ST_Distance(geomcol::geography,ST_SetSRID(ST_MakePoint(6.9333,46.8167),4326)::geography) < 30 * 1609.34
;

Aggregate  (cost=274167.33..274167.34 rows=1 width=0) (actual time=4940.531..4940.532 rows=1 loops=1)
  Output: count(*)
  ->  Seq Scan on bob.example1  (cost=0.00..273334.00 rows=333333 width=0) (actual time=592.766..4940.509 rows=11 loops=1)
        Output: idcol, geomcol
        Filter: (_st_distance((example1.geomcol)::geography, '0101000020E61000005D6DC5FEB2BB1B40545227A089684740'::geography, 0::double precision, true) < 48280.2::double precision)
        Rows Removed by Filter: 999989
Planning time: 2.137 ms
Execution time: 4940.568 ms

Bây giờ, nếu chúng ta sử dụng ST_DWithin, chúng ta vẫn quét toàn bộ bảng (mặc dù nhanh hơn):

EXPLAIN ANALYZE VERBOSE
SELECT  count(*)
FROM    example1
WHERE   ST_DWithin(geomcol::geography,ST_SetSRID(ST_MakePoint(6.9333,46.8167),4326)::geography,30 * 1609.34)
;

Aggregate  (cost=405867.33..405867.34 rows=1 width=0) (actual time=908.716..908.716 rows=1 loops=1)
  Output: count(*)
  ->  Seq Scan on bob.example1  (cost=0.00..405834.00 rows=13333 width=0) (actual time=38.449..908.700 rows=7 loops=1)
        Output: idcol, geomcol
        Filter: (((example1.geomcol)::geography && '0101000020E61000005D6DC5FEB2BB1B40545227A089684740'::geography) AND ('0101000020E61000005D6DC5FEB2BB1B40545227A089684740'::geography && _st_expand((example1.geomcol)::geography, 48280.2::double precision) (...)
        Rows Removed by Filter: 999993
Planning time: 2.017 ms
Execution time: 908.763 ms

Và đây là mảnh ghép cuối cùng - Xây dựng chỉ số bao phủ (địa lý diễn viên):

CREATE INDEX example1_gpx ON example1 USING GIST (geography(geomcol));
-- (Takes an extra 13 sec)

EXPLAIN ANALYZE VERBOSE
SELECT  count(*)
FROM    example1
WHERE   ST_DWithin(geomcol::geography,ST_SetSRID(ST_MakePoint(6.9333,46.8167),4326)::geography,30 * 1609.34)
;

Aggregate  (cost=96538.95..96538.96 rows=1 width=0) (actual time=0.775..0.775 rows=1 loops=1)
  Output: count(*)
  ->  Bitmap Heap Scan on bob.example1  (cost=8671.62..96505.62 rows=13333 width=0) (actual time=0.586..0.769 rows=19 loops=1)
        Output: idcol, geomcol
        Recheck Cond: ((example1.geomcol)::geography && '0101000020E61000005D6DC5FEB2BB1B40545227A089684740'::geography)
        Filter: (('0101000020E61000005D6DC5FEB2BB1B40545227A089684740'::geography && _st_expand((example1.geomcol)::geography, 48280.2::double precision)) AND _st_dwithin((example1.geomcol)::geography, '0101000020E61000005D6DC5FEB2BB1B40545227A089684740':: (...)
        Rows Removed by Filter: 14
        Heap Blocks: exact=33
        ->  Bitmap Index Scan on example1_gpx  (cost=0.00..8668.29 rows=200000 width=0) (actual time=0.384..0.384 rows=33 loops=1)
              Index Cond: ((example1.geomcol)::geography && '0101000020E61000005D6DC5FEB2BB1B40545227A089684740'::geography)
Planning time: 2.572 ms
Execution time: 0.820 ms

Cuối cùng, trình tối ưu hóa đang sử dụng chỉ số không gian và nó hiển thị, nhưng ba thứ tự cường độ giữa những người bạn là gì?

Một số hãy cẩn thận:

  • Tôi là một mọt sách cơ sở dữ liệu, vì vậy PC tại nhà của tôi có RAM 16Gb, sáu lõi 3,3Ghz và SSD 256Gb cho không gian bảng mặc định của cơ sở dữ liệu; số dặm của bạn có thể thay đổi

  • Tôi đã chạy lại SQL tạo trước mỗi truy vấn, để cân bằng sân chơi đối với các trang "nóng" trong bộ đệm, nhưng điều này có thể tạo ra kết quả hơi khác nhau vì cùng một hạt giống ngẫu nhiên không được sử dụng cho các lần chạy khác nhau

Và một lưu ý:

  • Tôi đã điều chỉnh phạm vi vĩ độ {-90, + 90} ban đầu để sử dụng cung-cosine cho phân bố diện tích bằng nhau (ít sai lệch về các cực)

1
Đây là một trong những câu trả lời hay nhất tôi từng có trong cộng đồng Stackexchange. Tôi vẫn chưa thử nhưng bạn đã cung cấp một ví dụ đầy đủ tôi có thể hiểu hoàn toàn. Cảm ơn bạn rất rất nhiều @Vince.
andrerpena

1
Có bất kỳ lý do tại sao không lưu trữ geomcol như địa lý? Cả ST_Distance và ST_DWithin đều mong đợi các khu vực địa lý. Và nếu chúng ta làm như vậy, chúng ta sẽ không cần thêm hình học đúc chỉ mục cho địa lý.
andrerpena

Đây là một câu hỏi khác nhau, và nếu được hỏi, có thể được đóng lại dưới dạng ý kiến.
Vince

1
Đã xem qua kết quả này trong google và cảm ơn bạn @Vince vì câu trả lời của bạn. Sự khác biệt nhỏ nhất của việc ép mạnh điểm địa lý vào geograhpy đã khiến thời gian truy vấn của tôi trung bình từ 43 giây đến 10ms thay vào đó ..
Angry 84

bài đăng tuyệt vời, nhưng tôi nghĩ `(acos (1.0 - 2 * Random ()) * 180.0) / pi ())` là không chính xác. phạm vi không nằm trong khoảng từ -90 đến 90
hxd1011
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.