Wie @ JohnPowellakaBarça sagte, ST_DWithin()
ist der richtige Weg, wenn Sie Korrektheit wollen .
In meinem Fall möchte ich jedoch nur eine grobe Schätzung, sodass diese ST_DWithin()
für meine Anforderungen sogar zu teuer (in Bezug auf die Abfragekosten) war. Ich habe stattdessen &&
und ST_Expand(box2d)
(nicht mit der geometry
Version verwechseln) verwendet. Beispiel:
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;
Was sofort offensichtlich sein wird, ist, dass es sich um Grad statt Meter handelt und wir einen Begrenzungsrahmen anstelle eines Kreises in einem Sphäroid verwenden. Für meinen Anwendungsfall reduziert sich dies von 24 ms auf nur 2 ms (lokal in SSD). Für meine Produktionsdatenbank in AWS RDS PostgreSQL mit gleichzeitigen Verbindungen und kaum großzügigen IOPS-Kontingenten (100 IOPS) gibt die ursprüngliche ST_DWithin()
Abfrage jedoch zu viel IOPS aus und kann über 2000 ms und viel schlimmer ausgeführt werden, wenn das IOPS-Kontingent erschöpft ist.
Dies ist nicht jedermanns Sache, aber falls Sie eine gewisse Genauigkeit für die Geschwindigkeit opfern können (oder um IOPS zu sparen), ist dieser Ansatz möglicherweise für Sie. Wie Sie in den folgenden Abfrageplänen sehen können, ist ST_DWithin
zusätzlich zu Recheck Cond weiterhin ein räumlicher Filter im Bitmap-Heap-Scan erforderlich, während &&
für eine Box-Geometrie kein Filter erforderlich ist und nur Recheck Cond verwendet wird.
Mir ist auch aufgefallen, dass es darauf IS NOT NULL
ankommt, ohne dass Sie einen schlechteren Abfrageplan haben. Es scheint, dass der GIST-Index dafür nicht "klug genug" ist. (Natürlich wird es nicht benötigt, wenn Ihre Kolumne ist NOT NULL
, in meinem Fall ist es in der NULL
Lage)
20000 Zeilentabelle ST_DWithin(geography, geography, 100000, FALSE)
auf AWS RDS 512 MB RAM mit 300 IOPS:
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 Zeilentabelle &&
und ST_Expand(box2d)
auf AWS RDS 512 MB RAM mit 300 IOPS:
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
Wieder mit einfacherer Abfrage:
20000 Zeilentabelle ST_DWithin(geography, geography, 100000, FALSE)
auf AWS RDS 512 MB RAM mit 300 IOPS:
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 Zeilentabelle &&
und ST_Expand(box2d)
auf AWS RDS 512 MB RAM mit 300 IOPS:
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