Pemindaian indeks lambat dalam tabel besar

12

Menggunakan PostgreSQL 9.2, saya memiliki masalah dengan pertanyaan lambat pada tabel yang relatif besar (200+ juta baris). Saya tidak mencoba sesuatu yang gila, hanya menambahkan nilai historis. Di bawah ini adalah kueri dan output rencana kueri.

Tata letak meja saya:

                                   Table "public.energy_energyentry"
  Column   |           Type           |                            Modifiers
-----------+--------------------------+-----------------------------------------------------------------
 id        | integer                  | not null default nextval('energy_energyentry_id_seq'::regclass)
 prop_id   | integer                  | not null
 timestamp | timestamp with time zone | not null
 value     | double precision         | not null
Indexes:
    "energy_energyentry_pkey" PRIMARY KEY, btree (id)
    "energy_energyentry_prop_id" btree (prop_id)
    "energy_energyentry_prop_id_timestamp_idx" btree (prop_id, "timestamp")
Foreign-key constraints:
    "energy_energyentry_prop_id_fkey" FOREIGN KEY (prop_id) REFERENCES gateway_peripheralproperty(id) DEFERRABLE INITIALLY DEFERRED

Data berkisar dari 2012-01-01 hingga sekarang, dengan data baru terus ditambahkan. Ada sekitar 2.2k nilai berbeda di prop_idkunci asing, didistribusikan secara merata.

Saya perhatikan bahwa perkiraan baris tidak jauh, tetapi perkiraan biaya tampak lebih besar dengan faktor 4x. Ini mungkin bukan masalah, tapi adakah yang bisa saya lakukan?

Saya berharap bahwa akses disk mungkin menjadi masalah, karena tabel tidak ada di memori sepanjang waktu.

EXPLAIN ANALYZE 
SELECT SUM("value") 
FROM "energy_energyentry" 
WHERE 
  "prop_id"=82411 
  AND "timestamp">'2014-06-11' 
  AND "timestamp"<'2014-11-11'
;
 Aggregate  (cost=214481.45..214481.46 rows=1 width=8) (actual time=51504.814..51504.814 rows=1 loops=1)
   ->  Index Scan using energy_energyentry_prop_id_timestamp_idx on  energy_energyentry (cost=0.00..214434.08 rows=18947 width=8) (actual time=136.030..51488.321 rows=13578 loops=1)
         Index Cond: ((prop_id = 82411) AND ("timestamp" > '2014-06-11 00:00:00+00'::timestamp with time zone) AND ("timestamp" < '2014-11-11 00:00:00+00'::timestamp with time zone))
 Total runtime: 51504.841 ms

Adakah saran bagaimana membuat ini lebih cepat?
Saya juga baik-baik saja dengan hanya mendengar saya tidak melakukan sesuatu yang aneh.

Exelian
sumber
1
Beri tahu kami seperti apa tabel Anda, indeks apa yang dimilikinya, dan penyebaran data.
Colin 't Hart
Saya menambahkan informasi tambahan yang Anda minta. Tidak tahu apakah saya melewatkan sesuatu.
Exelian
2
Aneh: Analisis penjelasan Anda menunjukkan prop_time_idx, namun definisi tabel menunjukkan entry_prop_id_timestamp_idx. Apakah ini indeks yang sama? Tolong perbaiki.
Colin 't Hart
Jika Anda merujuk dengan 'perkiraan biaya tampaknya menjadi faktor 4x lebih besar' dengan fakta bahwa angka biaya sekitar 4 kali lipat dari waktu aktual , maka harap perhatikan bahwa keduanya tidak ada hubungannya satu sama lain. Biaya hanya perkiraan, membantu pengoptimal kueri untuk memilih paket yang tampak terbaik. Di luar konteks ini, biasanya nilai tidak berarti.
dezso
1
Berapa persen tabel yang direpresentasikan oleh rentang tanggal Anda (tanpa mempertimbangkan nilainya prop)? Jika hanya sebagian kecil, mungkin indeks pada ("timestamp", prop)akan lebih baik. Beberapa indeks dengan kolom utama yang sama ( propdalam kasus Anda) juga seringkali berlebihan.
Colin 't Hart

Jawaban:

10

Meja Anda besar , dan indeks apa pun yang mencakup seluruh tabel. Berasumsi bahwa:

  • hanya data baru (dengan timestamp = now()) yang dimasukkan
  • baris yang ada tidak diubah atau dihapus.
  • Anda memiliki data sejak 2012-01-01 tetapi permintaan lebih banyak pada tahun berjalan (?)

Saya akan menyarankan indeks parsial, multi-kolom (meliputi!) :

CREATE INDEX ON energy_energyentry (prop_id, "timestamp", value)
WHERE "timestamp" >= '2014-01-01 0:0';  -- adapt to your needs

Hanya menyertakan rentang waktu yang diminta secara teratur. Efektivitas memburuk dari waktu ke waktu dengan entri baru. Buat ulang indeks dari waktu ke waktu. (Anda mungkin perlu menyesuaikan pertanyaan Anda.) Lihat jawaban yang ditautkan di bawah ini.

Nilai kolom terakhir hanya disertakan untuk mengeluarkan scan indeks saja . Pengaturan autovacuum yang agresif dapat membantu dengan menjaga agar peta visibilitas tetap terbaru, seperti @jjanes yang telah disebutkan .

Indeks parsial harus masuk ke dalam RAM lebih mudah dan tinggal di sana lebih lama.

Anda mungkin perlu memasukkan WHEREkondisi ini dalam kueri untuk membuat perencana memahami indeks ini berlaku untuk kueri, seperti:

SELECT sum(value) AS sum_value
FROM   energy_energyentry
WHERE  prop_id = 82411 
AND   "timestamp" > '2014-06-11 0:0' 
AND   "timestamp" < '2014-11-11 0:0'
AND   "timestamp" >= '2014-01-01 0:0'; -- seems redundant, but may be needed

Karena kueri Anda menjumlahkan banyak baris ( rows=13578), ini akan memakan waktu, bahkan dengan pemindaian hanya indeks. Seharusnya tidak berada di dekat 50 detik. Kurang dari satu detik pada perangkat keras setengah jalan yang layak.

Terkait (tetapi abaikan CLUSTERdan FILLFACTOR, keduanya tidak relevan jika Anda dapat memindai hanya indeks) :

Selain:
Karena Anda saat ini memiliki indeks aktif (prop_id, "timestamp"), indeks tambahan pada hanya (prop_id)mungkin lebih mahal daripada nilainya:

Erwin Brandstetter
sumber
Sekarang Postgres mendukung indeks BRIN, apakah itu berguna di sini? Saya berencana menyimpan sekitar 140 juta baris pada data di postgres, apakah BRIN indeks yang tepat untuk digunakan untuk tabel sebesar itu?
Arya
2

Jika Anda membuat indeks pada (prop_id, "timestamp", "value"), maka itu dapat menggunakan pemindaian hanya indeks untuk menghitung nilai tanpa pernah mengunjungi tabel. Ini bisa menghemat banyak akses disk acak.

Untuk mendapatkan manfaat maksimal, Anda harus agresif membersihkan meja. Pengaturan autovac default tidak cukup agresif untuk tabel hanya menyisipkan di mana Anda ingin secara efisien mendukung pemindaian hanya indeks.

jjanes
sumber
Menambahkan nilai mungkin memang menarik, saya akan melihat apakah itu akan mempercepat. Apakah Anda punya saran untuk pengaturan vakum atau dokumentasi yang bisa saya lihat?
Exelian