Saya memiliki tabel yang berisi sekitar 55 juta titik data (titik adalah geometri dengan SRID 4326) dan untuk permintaan saya, saya harus menggabungkan ini ke tabel area (saat ini memiliki ~ 1800 area) yang berisi berbagai variasi mulai dari poligon besar ( 2000 km persegi) hingga cukup kecil (kecil sekitar 100 km persegi).
Permintaan awal yang dipilih oleh pengguna mempersempit 55 juta poin awal menjadi sekitar ~ 300.000 poin tergantung pada rentang tanggal dll yang mereka pilih. Kemudian join selesai dan tergantung pada area yang mereka pilih untuk digunakan setelah query selesai, biasanya mempersempitnya menjadi ~ 150.000.
Masalah yang saya alami adalah bahwa beberapa kali kueri hanya terhenti dan alih-alih mengambil ~ 25 detik yang diharapkan dapat memakan waktu hingga ~ 18 menit. Pada titik ini biasanya harus melakukan ANALISA VAKUM dan kemudian menjalankan beberapa pertanyaan sebelum mulai berperilaku sendiri. Tidak ada data yang telah ditambahkan, diperbarui atau dihapus dari data atau tabel area pada saat ini.
Saya telah bermain-main dengan semua yang dapat saya pikirkan dan ini sepertinya masih terus terjadi tanpa keteguhan juga. Kolom data.point dan kolom area.polygon memiliki INDEKS GIST pada mereka.
Saya menemukan menghapus INDEX dari kolom data.point tampaknya membuat hal-hal sedikit lebih stabil tetapi lebih lambat ~ 35 detik secara normal. Namun menghapus INDEX tampaknya menjadi pilihan yang sangat buruk karena seharusnya tidak membantu tidak menghalangi?
Saya menggunakan PostgreSQL 9.1.4 dengan PostGIS 1.5
Inilah pertanyaan yang saya jalankan
select * FROM data, area WHERE st_intersects (data.point, area.polygon) AND
(readingdatetime BETWEEN '1948-01-01' AND '2012-11-19') AND datasetid IN(3) AND
"polysetID" = 1 AND area.id IN(28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11)
MENJELASKAN
Nested Loop (cost=312.28..336.59 rows=5 width=2246) (actual time=1445.973..11557.824 rows=12723 loops=1)
Join Filter: _st_intersects(data.point, area.polygon)
-> Index Scan using "area_polysetID_index" on area (cost=0.00..20.04 rows=1 width=1949) (actual time=0.017..0.229 rows=35 loops=1)
Index Cond: ("polysetID" = 1)
Filter: (id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
-> Bitmap Heap Scan on data (cost=312.28..316.29 rows=1 width=297) (actual time=328.771..329.136 rows=641 loops=35)
Recheck Cond: ((point && area.polygon) AND (datasetid = 3))"
Filter: ((readingdatetime >= '1948-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-11-19 00:00:00'::timestamp without time zone))
-> BitmapAnd (cost=312.28..312.28 rows=1 width=0) (actual time=328.472..328.472 rows=0 loops=35)
-> Bitmap Index Scan on data_point_index (cost=0.00..24.47 rows=276 width=0) (actual time=307.115..307.115 rows=1365770 loops=35)
Index Cond: (point && area.polygon)
-> Bitmap Index Scan on data_datasetid_index (cost=0.00..284.37 rows=12856 width=0) (actual time=1.522..1.522 rows=19486 loops=35)
Index Cond: (datasetid = 3)
Total runtime: 11560.879 ms
Tabel buat saya
CREATE TABLE data
(
id bigserial NOT NULL,
datasetid integer NOT NULL,
readingdatetime timestamp without time zone NOT NULL,
value double precision NOT NULL,
description character varying(255),
point geometry,
CONSTRAINT "DATAPRIMARYKEY" PRIMARY KEY (id ),
CONSTRAINT enforce_dims_point CHECK (st_ndims(point) = 2),
CONSTRAINT enforce_geotype_point CHECK (geometrytype(point) = 'POINT'::text OR point IS NULL),
CONSTRAINT enforce_srid_point CHECK (st_srid(point) = 4326)
);
CREATE INDEX data_datasetid_index ON data USING btree (datasetid);
ALTER TABLE data CLUSTER ON data_datasetid_index;
CREATE INDEX "data_datasetid_readingDatetime_index" ON data USING btree (datasetid , readingdatetime );
CREATE INDEX data_point_index ON data USING gist (point);
CREATE INDEX "data_readingDatetime_index" ON data USING btree (readingdatetime );
CREATE TABLE area
(
id serial NOT NULL,
polygon geometry,
"polysetID" integer NOT NULL,
CONSTRAINT area_primary_key PRIMARY KEY (id )
)
CREATE INDEX area_polygon_index ON area USING gist (polygon);
CREATE INDEX "area_polysetID_index" ON area USING btree ("polysetID");
ALTER TABLE area CLUSTER ON "area_polysetID_index";
Semoga semua masuk akal jika perlu tahu hal lain, silakan tanyakan.
Ringkasan singkat sebenarnya bahwa INDEKS tampaknya berfungsi beberapa kali tetapi tidak yang lain.
Adakah yang bisa menyarankan sesuatu yang saya bisa coba untuk mencari tahu apa yang terjadi?
Terima kasih sebelumnya.
EDIT:
Contoh lain
select * FROM data, area WHERE st_intersects ( data.point, area.polygon) AND
(readingdatetime BETWEEN '2009-01-01' AND '2012-01-19') AND datasetid IN(1,3) AND
"polysetID" = 1 AND area.id IN(28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11)
Jalankan pada salinan tabel dengan indeks titik
Nested Loop (cost=0.00..1153.60 rows=35 width=2246) (actual time=86835.883..803363.979 rows=767 loops=1)
Join Filter: _st_intersects(data.point, area.polygon)
-> Index Scan using "area_polysetID_index" on area (cost=0.00..20.04 rows=1 width=1949) (actual time=0.021..16.287 rows=35 loops=1)
Index Cond: ("polysetID" = 1)
Filter: (id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
-> Index Scan using data_point_index on data (cost=0.00..1133.30 rows=1 width=297) (actual time=17202.126..22952.706 rows=33 loops=35)
Index Cond: (point && area.polygon)
Filter: ((readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone) AND (datasetid = ANY ('{1,3}'::integer[])))
Total runtime: 803364.120 ms
Jalankan pada salinan tabel tanpa indeks titik
Nested Loop (cost=2576.91..284972.54 rows=34 width=2246) (actual time=181.478..235.608 rows=767 loops=1)
Join Filter: ((data_new2.point && area.polygon) AND _st_intersects(data_new2.point, area.polygon))
-> Index Scan using "area_polysetID_index" on area (cost=0.00..20.04 rows=1 width=1949) (actual time=0.149..0.196 rows=35 loops=1)
Index Cond: ("polysetID" = 1)
Filter: (id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
-> Bitmap Heap Scan on data_new2 (cost=2576.91..261072.36 rows=90972 width=297) (actual time=4.808..5.599 rows=2247 loops=35)
Recheck Cond: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
-> Bitmap Index Scan on "data_new2_datasetid_readingDatetime_index" (cost=0.00..2554.16 rows=90972 width=0) (actual time=4.605..4.605 rows=2247 loops=35)
Index Cond: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
Total runtime: 235.723 ms
Seperti yang Anda lihat permintaan secara signifikan lebih lambat ketika indeks titik sedang digunakan.
EDIT 2 (Pertanyaan yang Disarankan Paul):
WITH polys AS (
SELECT * FROM area
WHERE "polysetID" = 1 AND area.id IN(28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11)
)
SELECT *
FROM polys JOIN data ON ST_Intersects(data.point, polys.polygon)
WHERE datasetid IN(1,3)
AND (readingdatetime BETWEEN '2009-01-01' AND '2012-01-19');
MENJELASKAN
Nested Loop (cost=20.04..1155.43 rows=1 width=899) (actual time=16691.374..279065.402 rows=767 loops=1)
Join Filter: _st_intersects(data.point, polys.polygon)
CTE polys
-> Index Scan using "area_polysetID_index" on area (cost=0.00..20.04 rows=1 width=1949) (actual time=0.016..0.182 rows=35 loops=1)
Index Cond: ("polysetID" = 1)
Filter: (id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
-> CTE Scan on polys (cost=0.00..0.02 rows=1 width=602) (actual time=0.020..0.358 rows=35 loops=1)
-> Index Scan using data_point_index on data (cost=0.00..1135.11 rows=1 width=297) (actual time=6369.327..7973.201 rows=33 loops=35)
Index Cond: (point && polys.polygon)
Filter: ((datasetid = ANY ('{1,3}'::integer[])) AND (readingdatetime >= '2009-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2012-01-19 00:00:00'::timestamp without time zone))
Total runtime: 279065.540 ms
sumber
Jawaban:
Secara efektif memaksa perencana untuk melakukan hal yang Anda inginkan dapat membantu. Dalam hal ini, sub-setting tabel poligon sebelum mengeksekusi spasial bergabung dengan tabel poin. Anda mungkin bisa mengecoh perencana menggunakan sintaks "DENGAN":
Masalahnya dengan mencoba memainkan game-game ini adalah Anda memasukkan kode ke dalam pernyataan Anda dengan asumsi "daftar poligon saya akan selalu lebih selektif daripada bagian kueri saya yang lain". Yang mungkin tidak benar untuk semua parameterisasi kueri Anda, atau untuk semua aplikasi kueri tertentu di atas set data yang terdistribusi secara heterogen.
Tapi itu mungkin berhasil.
UPDATE : Ini lebih jauh menyusuri jalan berbahaya dengan asumsi Anda tahu selektivitas klausa Anda sebelumnya, kali ini kami juga mengambil pemilihan atribut pada tabel titik dan melakukannya secara terpisah sebelum spasial bergabung:
sumber
Dengan melihat penjelasan untuk Jalankan pada salinan tabel dengan indeks titik pada EDIT pertama sepertinya Anda kehilangan indeks ini di atas meja tanpa indeks titik:
Bisakah Anda mengkonfirmasi bahwa ada indeks?
- EDIT -
Setelah lebih banyak mempelajari pertanyaan Anda (bukan yang mudah, btw) saya punya saran berikut untuk membuat.
mengelompokkan tabel terhadap indeks "data_readingDatetime_index". Clustering lebih efektif dengan kueri berbasis rentang. Anda sepertinya tidak meminta datasetid dengan kondisi berbasis rentang
Lakukan pengelompokan yang sebenarnya. Perintah pada item sebelumnya tidak mengelompokkan tabel Anda, itu hanya mengekspresikan keinginan Anda bahwa jika tabel itu akan dikelompokkan, Anda ingin dikelompokkan pada indeks itu. Gugus dengan:
menganalisis tabel setelah mengelompokkannya sehingga statistik (digunakan oleh perencana untuk memutuskan strategi mana yang harus dipilih) perhatikan tata letak baru pada disk:
sekarang karena data disusun berdasarkan waktu baca, perencana akan memilih strategi di mana indeks data_readingDatetime_index digunakan dan karena setiap kali digunakan, jelaskan rencana yang tampaknya yang tercepat maka mungkin kinerja akan meningkat dan berfluktuasi lebih sedikit
Seperti yang saya katakan di komentar untuk jawaban Paul di atas, jangan berpikir bahwa perencana tidak akan mengubah strategi tergantung pada filter (bahkan jika filter selalu sama dan hanya nilainya yang berubah).
Ada sebuah contoh dalam buku PostregSQL 9.0 High Performance yang sangat direkomendasikan di mana mengubah kondisi dari pilih ... dari tabel t di mana v <5 ke v <6 mengalihkan rencana dari pemindaian indeks ke pemindaian tabel penuh.
sumber