Perbarui tampilan yang dibuat secara bertahap di PostgreSQL

33

Apakah mungkin untuk me-refresh tampilan terwujud secara bertahap di PostgreSQL yaitu hanya untuk data yang baru atau telah berubah?

Pertimbangkan tabel ini & tampilan terwujud:

CREATE TABLE graph (
   xaxis integer NOT NULL,
   value integer NOT NULL,
);

CREATE MATERIALIZED VIEW graph_avg AS 
SELECT xaxis, AVG(value)
FROM graph
GROUP BY xaxis

Secara berkala, nilai-nilai baru ditambahkan graphatau nilai yang ada diperbarui. Saya ingin menyegarkan tampilan graph_avgsetiap beberapa jam hanya untuk nilai yang telah diperbarui. Namun dalam PostgreSQL 9.3, seluruh tabel di-refresh. Ini cukup memakan waktu. Versi berikutnya 9.4 memungkinkan CONCURRENTpembaruan tetapi masih menyegarkan seluruh tampilan. Dengan 100-an juta baris, ini membutuhkan waktu beberapa menit.

Apa cara yang baik untuk melacak nilai yang diperbarui & baru dan hanya menyegarkan tampilan sebagian?

pengguna4150760
sumber

Jawaban:

22

Anda selalu dapat menerapkan tabel Anda sendiri yang berfungsi sebagai "tampilan terwujud". Itulah yang harus Anda lakukan sebelum MATERIALIZED VIEWditerapkan di Postgres 9.3.

Misalnya, Anda dapat membuat polos VIEW:

CREATE VIEW graph_avg_view AS 
SELECT xaxis, AVG(value) AS avg_val
FROM   graph
GROUP  BY xaxis;

Dan terwujud hasilnya secara keseluruhan sekali atau kapan pun Anda perlu memulai dari awal:

CREATE TABLE graph_avg AS
SELECT * FROM graph_avg_view

(Atau gunakan SELECTpernyataan secara langsung, tanpa membuat a VIEW.)
Kemudian, tergantung pada detail yang tidak diketahui dari use case Anda, Anda bisa DELETE/ UPDATE/ INSERTberubah secara manual.

Pernyataan DML dasar dengan CTE pemodifikasi data untuk tabel Anda adalah :

Dengan asumsi tidak ada mencoba lagi untuk menulis untuk graph_avgsecara bersamaan (membaca adalah tidak ada masalah):

WITH del AS (
   DELETE FROM graph_avg t
   WHERE  NOT EXISTS (SELECT 1 FROM graph_avg_view v WHERE v.xaxis = v.xaxis);
   )
, upd AS (
   UPDATE graph_avg t
   FROM   graph_avg_view v
   WHERE  t.xaxis = v.xaxis
   AND    t.avg_val <> v.avg_val
   )
INSERT INTO graph_avg t
SELECT *
FROM   graph_avg_view v
LEFT   JOIN graph_avg t USING (xaxis)
WHERE  t.xaxis IS NULL;

Tetapi ini kemungkinan besar harus dioptimalkan.

Resep dasar:

  • Tambahkan timestampkolom dengan default now()ke tabel basis Anda. Sebut saja ts.
    • Jika Anda memiliki pembaruan, tambahkan pemicu untuk mengatur cap waktu saat ini dengan setiap pembaruan yang berubah baik xaxisatau value.
  • Buat tabel kecil untuk mengingat stempel waktu dari foto terbaru Anda. Sebut saja mv:

    CREATE TABLE mv (
       tbl text PRIMARY KEY
     , ts timestamp NOT NULL DEFAULT '-infinity'
    ); -- possibly more details
    
  • Buat parsial, indeks multikolom ini:

    CREATE INDEX graph_mv_latest ON graph (xaxis, value)
    WHERE  ts >= '-infinity';
    
  • Gunakan cap waktu snapshot terakhir sebagai predikat dalam kueri Anda untuk menyegarkan snapshot dengan penggunaan indeks yang sempurna.

  • Di akhir transaksi, lepaskan indeks dan buat kembali dengan stempel waktu transaksi menggantikan stempel waktu dalam predikat indeks (awalnya '-infinity'), yang juga Anda simpan ke meja Anda. Semuanya dalam satu transaksi.

  • Perhatikan bahwa indeks parsial bagus untuk dibahas INSERTdan UPDATEdioperasikan, tetapi tidak DELETE. Untuk mengatasinya, Anda perlu mempertimbangkan seluruh tabel. Itu semua tergantung pada persyaratan yang tepat.

Erwin Brandstetter
sumber
Terima kasih atas kejelasan pandangan terwujud dan menyarankan jawaban alternatif.
user4150760
13

Pembaruan Serentak (Postgres 9.4)

Meskipun bukan pembaruan tambahan seperti yang Anda minta, Postgres 9.4 memang menyediakan fitur pembaruan bersamaan yang baru .

Mengutip dokumen ...

Sebelum PostgreSQL 9.4, menyegarkan tampilan terwujud berarti mengunci seluruh tabel, dan karenanya mencegah permintaan apa pun, dan jika pembaruan membutuhkan waktu lama untuk mendapatkan kunci eksklusif (saat menunggu kueri menggunakannya untuk menyelesaikannya), pada gilirannya memegang pertanyaan selanjutnya. Ini sekarang dapat dikurangi dengan kata kunci CONCURRENTLY:

 postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;

Namun, indeks yang unik perlu ada pada tampilan terwujud. Alih-alih mengunci tampilan terwujud, alih-alih membuat versi yang diperbarui sementara, membandingkan dua versi, lalu menerapkan INSERT dan HAPUS terhadap tampilan terwujud untuk menerapkan perbedaan. Ini berarti kueri masih dapat menggunakan tampilan terwujud saat sedang diperbarui. Tidak seperti bentuk non-konkuren, tupel tidak beku, dan perlu VACUUM karena DELETE yang disebutkan sebelumnya yang akan meninggalkan tupel mati.

Pembaruan bersamaan ini masih menjalankan kueri baru yang lengkap (bukan tambahan). Jadi CONCURRENTLY tidak menghemat waktu perhitungan keseluruhan, itu hanya meminimalkan jumlah waktu tampilan terwujud Anda tidak tersedia untuk digunakan selama pembaruan.

Basil Bourque
sumber
11
Sejenak aku bersemangat sampai aku membaca dengan cermat. it instead creates a temporary updated version of it...compares the two versions- Ini berarti versi sementara yang diperbarui masih merupakan perhitungan penuh, maka itu berlaku perbedaan untuk tampilan yang ada. Jadi intinya, saya masih melakukan kembali SEMUA perhitungan, tetapi hanya di tabel sementara.
user4150760
5
Ah, benar, CONCURRENTLYtidak menghemat waktu perhitungan keseluruhan, itu hanya meminimalkan jumlah waktu tampilan terwujud Anda tidak tersedia untuk digunakan selama pembaruan.
Basil Bourque