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ó geometry
các cột sử dụng dữ liệu địa lý WGS 1984 (SRID 4326):
- Sử dụng
ST_DWithin
chứ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
- 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_DWithin
có 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)
ST_SetSRID()
choST_MakePoint
trước khi truyền vào địa lý trong truy vấn.