Saya mencoba meningkatkan kinerja untuk kueri di bawah ini. Tidak peduli bagaimana saya menulis kueri (subquery dalam klausa FROM, subquery dalam klausa WHERE) postgres bersikeras menjalankan semua ~ 570K baris melalui fungsi ST_DWITHIN yang mahal walaupun hanya ada 60 baris di mana county = 24. Bagaimana saya bisa mendapatkan postgres untuk difilter pada county = 24 SEBELUM menjalankan postgis func yang menurut saya akan jauh lebih cepat dan jauh lebih efisien? 700ms bukan penyebab terlalu banyak kekhawatiran, tetapi karena tabel ini tumbuh hingga 10 juta + saya khawatir tentang kinerja.
Juga untuk dicatat, p.id adalah kunci utama, p.zipcode adalah indeks fk, z.county adalah indeks fk, dan p.geom memiliki indeks GiST.
Pertanyaan:
EXPLAIN ANALYZE
SELECT count(p.id)
FROM point AS p
LEFT JOIN zipcode AS z
ON p.zipcode = z.zipcode
WHERE z.county = 24
AND ST_DWithin(
p.geom,
ST_SetSRID(ST_Point(-121.479756008715,38.563236291512),4269),
16090.0,
false
)
MENJELASKAN ANALISIS:
Aggregate (cost=250851.91..250851.92 rows=1 width=4) (actual time=724.007..724.007 rows=1 loops=1)
-> Hash Join (cost=152.05..250851.34 rows=228 width=4) (actual time=0.359..723.996 rows=51 loops=1)
Hash Cond: ((p.zipcode)::text = (z.zipcode)::text)
-> Seq Scan on point p (cost=0.00..250669.12 rows=7437 width=10) (actual time=0.258..723.867 rows=63 loops=1)
Filter: (((geom)::geography && '0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography) AND ('0101000020AD10000063DF8B52B45E5EC070FB752018484340'::geography && _st_expand((geom)::geography, 16090::double precision)) AND _st_dwithin((g (...)
Rows Removed by Filter: 557731
-> Hash (cost=151.38..151.38 rows=54 width=6) (actual time=0.095..0.095 rows=54 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 3kB
-> Bitmap Heap Scan on zipcode z (cost=4.70..151.38 rows=54 width=6) (actual time=0.023..0.079 rows=54 loops=1)
Recheck Cond: (county = 24)
Heap Blocks: exact=39
-> Bitmap Index Scan on fki_zipcode_county_foreign_key (cost=0.00..4.68 rows=54 width=0) (actual time=0.016..0.016 rows=54 loops=1)
Index Cond: (county = 24)
Planning time: 0.504 ms
Execution time: 724.064 ms
point
baris ~ 60 di mana county = 24 ke tabel baru sendiri, permintaan hanya membutuhkan 0,453ms dibandingkan dengan 724 sehingga pasti ada perbedaan besar.count(*)
gaya. Jikaid
pkid seperti yang Anda katakan, ituNOT NULL
artinya mereka sama. Kecualicount(id)
memiliki kelemahan yang harus Anda ajukan pertanyaan itu jika tidakid
dapat dibatalkan.Jawaban:
Anda dapat melihat masalah dengan jumlah baris yang diharapkan vs jumlah aktual. Perencana berpikir bahwa ada 7.437 baris, tetapi hanya ada 63. Statistik tidak aktif. Cukup menarik juga, itu tidak menggunakan indeks kotak terikat (indeks) pencarian dengan
DWithin
Anda dapat menempelkan hasilnya\d point
. Versi PostGIS dan PostgreSQL apa?Coba jalankan
ANALYZE point
. Apakah Anda mendapatkan rencana yang sama saat Anda menaikkan kondisinya?sumber
Sebagai catatan tambahan, ada kemungkinan yang masuk akal bahwa perilaku ini dimodifikasi di PostGIS 2.3.0 jika Anda ingin menyebutnya bug.
Dari dokumen di PostgreSQL
Jadi biaya standarnya adalah 1 (sangat murah).
D_Within
menggunakan indeks GIST sangat murah. Tapi, itu meningkat menjadi 100 (dengan proxy internal_ST_DWithin
).Saya sendiri bukan penggemar berat metode CTE. CTE adalah pagar optimasi. Jadi melakukan ini sedemikian rupa menghilangkan beberapa ruang potensial untuk optimasi di masa depan. Jika standar saner memperbaikinya, saya lebih suka memutakhirkan. Pada akhirnya, kami harus menyelesaikan pekerjaan dan metode itu jelas bekerja untuk Anda.
sumber
Berkat petunjuk John Powell, saya merevisi kueri untuk menempatkan kondisi pembatasan daerah dalam kueri with / CTE dan kinerja yang ditingkatkan ini sedikit ke 222ms vs 700. Masih jauh sekali dari 0,74 ms yang saya dapatkan ketika data ada di dalam meja sendiri. Saya masih tidak yakin mengapa perencana tidak membatasi set data sebelum menjalankan melalui fungsi postgis yang mahal, dan saya harus mencoba dengan dataset yang lebih besar ketika saya memilikinya tetapi ini tampaknya menjadi solusi untuk situasi unik ini untuk saat ini.
sumber
Anda harus membuat indeks aktif
zipcode(county, zipcode)
, yang akan memberi Anda hanya memindai indeks pada z.Anda mungkin juga ingin bereksperimen dengan
btree_gist
ekstensi yang membuatpoint(zipcode, geom)
indeks ataupoint(geom, zipcode)
danzipcode(zipcode, county)
indeks.sumber