Seperti yang dikatakan JohnPowellakaBarça ST_DWithin()
adalah cara untuk pergi ketika Anda menginginkan kebenaran .
Namun dalam kasus saya, saya hanya ingin perkiraan kasar sehingga bahkan ST_DWithin()
terlalu mahal (dalam biaya permintaan) untuk kebutuhan saya. Saya menggunakan &&
dan ST_Expand(box2d)
(jangan salah ini dengan geometry
versi) sebagai gantinya. Contoh:
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;
Apa yang akan segera jelas adalah bahwa kita berhadapan dengan derajat bukannya meter, dan menggunakan kotak pembatas alih-alih lingkaran dalam sebuah bola bulat. Untuk kasus penggunaan saya, ini mengurangi dari 24 ms menjadi hanya 2 ms (secara lokal di SSD). Namun untuk basis data produksi saya di AWS RDS PostgreSQL dengan koneksi bersamaan dan kuota IOPS yang sangat murah (100 IOPS), ST_DWithin()
kueri asli menghabiskan terlalu banyak IOPS dan dapat mengeksekusi lebih dari 2000 ms dan jauh lebih buruk ketika kuota IOPS habis.
Ini bukan untuk semua orang tetapi jika Anda dapat mengorbankan akurasi untuk kecepatan (atau untuk menyelamatkan IOPS), maka pendekatan ini mungkin untuk Anda. Seperti yang Anda lihat dalam rencana kueri di bawah ini, ST_DWithin
masih memerlukan Filter spasial di dalam Bitmap Heap Scan selain Recheck Cond, sedangkan &&
pada kotak geometri tidak memerlukan Filter dan hanya menggunakan Recheck Cond.
Saya juga memperhatikan bahwa yang IS NOT NULL
penting, tanpa itu Anda akan dibiarkan dengan rencana kueri yang lebih buruk. Tampaknya indeks GIST tidak "cukup pintar" untuk ini. (tentu saja itu tidak diperlukan jika kolom Anda NOT NULL
, dalam kasus saya itu NULL
bisa)
20000 baris tabel, ST_DWithin(geography, geography, 100000, FALSE)
pada AWS RDS 512 MB RAM dengan 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 baris tabel, &&
dan ST_Expand(box2d)
pada AWS RDS 512 MB RAM dengan 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
Lagi dengan permintaan yang lebih sederhana:
20000 baris tabel, ST_DWithin(geography, geography, 100000, FALSE)
pada AWS RDS 512 MB RAM dengan 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 baris tabel, &&
dan ST_Expand(box2d)
pada AWS RDS 512 MB RAM dengan 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