PostGIS Intermittent INDEX Performance

8

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
Mark Davidson
sumber
Anda berbicara tentang "kueri" seolah-olah itu SQL yang sama persis setiap saat. Apakah itu?
Paul Ramsey
1
Selain apa yang telah dikatakan Paul di atas, bagaimana dengan memposting hasil dari MENJELASKAN kueri? postgresql.org/docs/8.1/static/sql-explain.html
Kelso
@PaulRamsey Tidak, kueri tidak sama setiap kali, seperti yang saya katakan tergantung pada input yang dipilih pengguna. Namun saya tidak mengurangi pertanyaan saya dengan itu karena tidak membuat perbedaan apa filter-filter itu, yah itu hanya mempengaruhi seperti yang Anda harapkan jika ada lebih sedikit baris karena kendala ketat lebih cepat dan sebaliknya sebaliknya. Tetapi itu akan terpengaruh dengan cara yang sama seperti di dalamnya beberapa kali dapat mulai berjalan sangat lambat sampai melakukan ANALISIS VACCUM seperti yang saya jelaskan di atas, tidak peduli apa pun kueri itu.
Mark Davidson
@ Selso Maaf lupa menambahkan bahwa saya akan menambahkan EXPLAIN sebentar lagi.
Mark Davidson
Telah menambahkan EXPLAIN dan beberapa bit lainnya.
Mark Davidson

Jawaban:

4

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":

WITH polys AS (
  SELECT * FROM area
  WHERE area.id in 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(3) 
AND (readingdatetime BETWEEN '1948-01-01' AND '2012-11-19');

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:

WITH polys AS (
  SELECT * FROM area
  WHERE area.id in 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)
),
WITH points AS (
  SELECT * FROM data
  WHERE datasetid IN(3) 
  AND (readingdatetime BETWEEN '1948-01-01' AND '2012-11-19')
)
SELECT * 
FROM polys JOIN points ON ST_Intersects(points, polys.polygon);
Paul Ramsey
sumber
Memperbarui pertanyaan saya dengan kueri yang disarankan dan paul hasil EXPLAIN. Tampaknya berkinerja lebih baik tetapi masih tidak ada yang dekat sebaik tanpa indeks. Saya melihat pertanyaan dan bertanya-tanya apakah itu ada hubungannya dengan fakta bahwa itu mencoba untuk bekerja jika semua poin ada dalam poligon sebelum mempersempit waktu membaca dan dataset atau apakah saya salah memahami EXPLAIN?
Mark Davidson
Bisakah Anda mengklarifikasi dari tabel apa waktu baca dan dataset berasal?
Paul Ramsey
Nevermind, saya melihatnya di DDL. Masalahnya sekarang cukup jelas, saya pikir, dan itu ke selektivitas bergabung kembali terlalu selektif. Saya tidak memperhatikan bahwa klausa Anda yang lain mengesampingkan tabel poin. Dorong filter non-spasial menjadi klausa DENGAN juga.
Paul Ramsey
Ini tampak paul yang sangat menjanjikan setelah mendorong sisa pertanyaan non-spasial ke dalam klausa DENGAN. Saya sudah tidak masuk kerja selama 2 hari terakhir, jadi belum memiliki kesempatan untuk 100% mengkonfirmasi bahwa ini menyelesaikan masalah, tapi saya akan memberi Anda hadiah karena semua saran Anda baik di sini maupun pada pengguna postgis daftar telah sangat membantu. Akan melaporkan kembali setelah saya tahu pasti.
Mark Davidson
2

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:

CREATE INDEX "data_readingDatetime_index" ON data USING btree (readingdatetime );

Bisakah Anda mengkonfirmasi bahwa ada indeks?

- EDIT -

Setelah lebih banyak mempelajari pertanyaan Anda (bukan yang mudah, btw) saya punya saran berikut untuk membuat.

  1. letakkan indeks "data_datasetid_readingDatetime_index" karena Anda telah mengindeks kedua kolom secara terpisah. Ini akan menghemat ruang Anda, meningkatkan kinerja penyisipan dan akan menyederhanakan pekerjaan perencana kueri dengan mengeluarkan satu variabel dari persamaan
  2. mengelompokkan tabel terhadap indeks "data_readingDatetime_index". Clustering lebih efektif dengan kueri berbasis rentang. Anda sepertinya tidak meminta datasetid dengan kondisi berbasis rentang

    ALTER TABLE data CLUSTER ON data_readingDatetime_index;
  3. 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:

     CLUSTER data;
  4. menganalisis tabel setelah mengelompokkannya sehingga statistik (digunakan oleh perencana untuk memutuskan strategi mana yang harus dipilih) perhatikan tata letak baru pada disk:

     VACUUM ANALYZE data;

    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.

unicoletti
sumber
Jika Anda melihat penjelasan ketiganya, Anda akan melihat "-> Indeks Pemindaian Bitmap pada" data_new2_datasetid_readingDatetime_index "(biaya = 0,00..2554.16 baris = lebar 90972 = 0) (waktu aktual = 4.605..4.605 baris = 2.247 putaran = 3547 loop = 35 ) "yang merupakan tempat indeks pada kolom itu benar-benar berperan setelah indeks spasial dikeluarkan dari persamaan.
Paul Ramsey
tepat maksud saya. itu ada di permintaan cepat, bukan di yang lambat (yang lambat menjadi yang di jelaskan rencana sebelum yang ketiga yang Anda sebutkan). Mungkinkah dalam menyalin tabel indeks hilang?
unicoletti
Tidak, seperti disebutkan di atas, perencana melewatkan indeks yang mendukung indeks spasial, karena indeks spasial (secara keliru) melaporkan selektivitas yang sangat tinggi kepada perencana. Jadi itu ada, itu tidak digunakan.
Paul Ramsey
@unicoletti Terima kasih banyak atas masukan Anda dan tentu saja akan memberi Anda saran dan akan segera melaporkan hasilnya. Saya benar-benar mendapatkan buku PostgreSQL 9.0 High Performance yang sangat setuju, sangat merekomendasikan membaca, tahu saya perlu membacanya sendiri untuk memastikan mendapatkan setiap peningkatan kinerja kecil yang saya bisa.
Mark Davidson