geometry
WGS 1984 coğrafi verilerini (SRID 4326) kullanan sütunları olan büyük tablolarda iyi bir jeodezik sorgu performansı elde etmenin iki anahtarı vardır :
- Kullanılabilir
ST_DWithin
bir uzamsal dizin kullanarak arama yapan ve Kartezyen mesafeli coğrafya özelliklerini bulan işlevi kullanın
- Cast coğrafyası üzerinde ekstra bir dizin oluşturun, böylece
ST_DWithin
kullanabilirsiniz
Öyleyse gerçek dünyada neler olduğuna bakalım. İlk önce bir milyon rastgele noktadan oluşan bir tablo oluşturmalı ve doldurmalıyız:
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)
ST_Distance sorgusunu yürütürsek, beklenen tam tablo taramasını alırız:
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
Kullandığımız Şimdi, eğer ST_DWithin
biz hala (daha hızlı bir de olsa) tam tablo taraması olsun:
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
Ve bu son parça - Kaplama endeksinin oluşturulması (döküm coğrafyası):
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
Son olarak, optimizer uzamsal endeksi kullanıyor ve gösteriyor, ancak arkadaşlar arasındaki üç büyüklük sırası nedir?
Bazı uyarılar:
Ben bir veritabanı nerd değilim, bu yüzden ev bilgisayarım 16Gb RAM, altı 3.3Ghz çekirdek ve veritabanı varsayılan tablo alanı için 256Gb SSD var; kilometreniz değişebilir
Önbellekteki "sıcak" sayfalara göre oyun alanını düzeltmek için her sorgudan önce oluşturma SQL'i yeniden çalıştırdım, ancak aynı rastgele tohum farklı çalışmalar için kullanılmadığı için bu biraz farklı sonuçlar üretebilir
Ve bir not:
- Eşit alan dağılımı için kutup kosinüsü kullanmak için orijinal {-90, + 90} enlem aralığını değiştirdim (kutuplara daha az önyargılı)
ST_SetSRID()
içinST_MakePoint
sorguda coğrafyaya döküm önce.