Memperbarui PostgreSQL tampilan yang di-materialisasi dapat menjadi lambat ketika Anda memproses sejumlah besar data, seperti miliaran catatan. Rendahnya kesegaran data memengaruhi efisiensi analisis intelijen bisnis (BI) dan pelaporan. Fitur Indeks Kolom dalam Memori (IMCI) di PolarDB untuk PostgreSQL secara dramatis mengurangi waktu yang diperlukan untuk memperbarui tampilan yang di-materialisasi. Ini meningkatkan kesegaran data dan performa beban kerja BI dan pelaporan Anda.
Ikhtisar Solusi
IMCI adalah mesin akselerasi analitik yang disediakan oleh PolarDB untuk PostgreSQL. Ini membuat indeks penyimpanan kolom untuk tabel penyimpanan baris dan secara otomatis menjaga konsistensi antara data penyimpanan baris dan indeks penyimpanan kolom. Saat menjalankan agregasi atau penggabungan kompleks, database dapat menggunakan IMCI untuk komputasi, memberikan performa yang jauh melampaui pemindaian penyimpanan baris tradisional.
Inti dari solusi ini adalah membuat IMCI untuk tabel dasar dari tampilan yang di-materialisasi. Ini mempercepat pembuatan dan pembaruan tampilan yang di-materialisasi.
Prasyarat
Versi Kluster:
PostgreSQL 16 (versi mesin minor 2.0.16.8.3.0 atau lebih baru)
PostgreSQL 14 (versi mesin minor 2.0.14.10.20.0 atau lebih baru)
CatatanAnda dapat melihat versi mesin minor di konsol atau menggunakan pernyataan
SHOW polardb_version;. Jika kluster Anda tidak memenuhi persyaratan versi mesin minor, Anda harus meningkatkan versi mesin minor.Tabel sumber harus memiliki kunci utama. Kolom kunci utama harus disertakan saat Anda membuat indeks penyimpanan kolom.
Parameter
wal_levelharus diatur kelogical. Pengaturan ini menambahkan informasi yang diperlukan untuk pengkodean logis ke pencatatan log terlebih dahulu (WAL).CatatanAnda dapat mengatur parameter wal_level di konsol. Untuk informasi lebih lanjut, lihat Atur parameter wal_level. Kluster akan dimulai ulang setelah parameter ini diubah. Rencanakan operasi bisnis Anda dengan hati-hati sebelum melakukan perubahan ini.
Catatan
Anda hanya dapat membuat satu indeks penyimpanan kolom untuk setiap tabel.
Anda tidak dapat memodifikasi indeks penyimpanan kolom. Untuk menambahkan kolom ke indeks penyimpanan kolom, Anda harus membangun ulang indeks tersebut.
Persiapan
Siapkan lingkungan
Siapkan kluster PolarDB untuk PostgreSQL yang memenuhi prasyarat.
Aktifkan IMCI. Metode untuk mengaktifkannya bervariasi berdasarkan versi mesin minor kluster PolarDB untuk PostgreSQL Anda:
Instal ekstensi pg_hint_plan di sistem bisnis Anda. Ekstensi ini memungkinkan Anda menyesuaikan rencana eksekusi menggunakan petunjuk dalam komentar khusus.
CREATE EXTENSION pg_hint_plan;Buat ekstensi pg_cron (tugas terjadwal) di database sistem
postgres. Ekstensi ini memungkinkan Anda menjalankan tugas secara otomatis pada waktu atau interval tertentu.Beralih ke database.
\c postgres;Instal ekstensi.
CREATE EXTENSION pg_cron;
Siapkan data
Di sistem bisnis Anda, buat tabel customers dan orders, buat IMCI untuk mereka, dan masukkan data uji.
Beralih ke sistem bisnis Anda. Contoh ini menggunakan
testdb.\c testdb;Buat tabel dan masukkan data.
-- Buat tabel customers dan IMCI. CREATE TABLE customers ( customer_id SERIAL PRIMARY KEY, customer_name VARCHAR(100), email VARCHAR(100) ); CREATE INDEX idx_customers_csi ON customers USING csi; -- Buat tabel orders dan IMCI. CREATE TABLE orders ( order_id SERIAL PRIMARY KEY, order_date DATE, amount DECIMAL(10, 2), customer_id INT REFERENCES customers(customer_id) ); CREATE INDEX idx_orders_csi ON orders USING csi; -- Masukkan data ke tabel customers. INSERT INTO customers (customer_name, email) VALUES ('Alice', 'alice@example.com'), ('Bob', 'bob@example.com'), ('Charlie', 'charlie@example.com'); -- Masukkan data ke tabel orders. INSERT INTO orders (order_date, amount, customer_id) VALUES ('2025-06-01', 200.00, 1), ('2025-06-02', 150.00, 2), ('2025-06-03', 300.00, 1), ('2025-06-04', 100.00, 3);
Buat tampilan yang di-materialisasi
Saat Anda membuat tampilan yang di-materialisasi, gunakan petunjuk untuk memaksa pengoptimal kueri menggunakan indeks penyimpanan kolom untuk komputasi.
/*+ SET(polar_csi.enable_query on) SET(polar_csi.cost_threshold 0) SET(polar_csi.exec_parallel 6) SET(polar_csi.memory_limit 10240) */CREATE MATERIALIZED VIEW mv_customer_orders AS
SELECT
c.customer_name AS customer_name,
o.order_date AS order_date,
o.amount AS amount
FROM
orders o
JOIN
customers c ON o.customer_id = c.customer_id;Parameter Petunjuk
Parameter | Deskripsi |
| Mengizinkan kueri menggunakan indeks penyimpanan kolom. |
| Mengatur ambang batas biaya ke 0 untuk memaksa penggunaan indeks penyimpanan kolom. |
| Mengatur tingkat paralelisme untuk komputasi indeks penyimpanan kolom. Kami merekomendasikan agar nilai ini tidak melebihi jumlah core CPU pada node. |
| Mengatur memori yang tersedia untuk komputasi dalam MB. |
Perbarui tampilan yang di-materialisasi
Buat fungsi untuk memperbarui tampilan yang di-materialisasi
Kapsulasi proses pembaruan dalam sebuah fungsi. Kami merekomendasikan Anda menggunakan fungsi berikut untuk pembaruan karena dapat mengganti tampilan lama dengan aman sambil tetap mempertahankan indeks dan kepemilikannya.
Fungsi berikut hanya untuk referensi. Meskipun memastikan alih bencana yang aman, Anda harus sepenuhnya mengujinya sebelum menggunakannya di lingkungan produksi.
-- view_name adalah nama tampilan yang di-materialisasi, schema_name adalah skema tempat tampilan yang di-materialisasi berada (default adalah current_schema), dan new_owner adalah pemilik dari tampilan yang di-materialisasi yang baru dibuat.
CREATE OR REPLACE FUNCTION refresh_materialized_view_safely_using_csi(
view_name TEXT,
schema_name TEXT DEFAULT NULL,
new_owner TEXT DEFAULT NULL
)
RETURNS BOOL
LANGUAGE plpgsql
AS $$
DECLARE
view_definition TEXT;
new_view_name TEXT;
old_view_name TEXT;
index_record RECORD;
index_creation_sql TEXT;
explain_result TEXT;
target_schema TEXT;
qualified_old_name TEXT;
qualified_new_name TEXT;
current_owner TEXT;
grant_record RECORD;
BEGIN
-- Tentukan skema target (gunakan parameter input atau skema saat ini).
IF schema_name IS NULL THEN
target_schema := current_schema();
ELSE
target_schema := schema_name;
END IF;
-- Buat nama tabel yang memenuhi syarat sepenuhnya.
qualified_old_name := format('%I.%I', target_schema, view_name);
qualified_new_name := format('%I.%I', target_schema, view_name || '_new');
RAISE NOTICE 'Beroperasi di skema: %', target_schema;
-- Verifikasi bahwa tampilan yang di-materialisasi ada.
IF NOT EXISTS (
SELECT 1 FROM pg_matviews
WHERE matviewname = view_name
AND schemaname = target_schema
) THEN
RAISE EXCEPTION 'Tampilan yang di-materialisasi "%" tidak ada di skema "%"', view_name, target_schema;
END IF;
-- Dapatkan definisi dan pemilik saat ini dari tampilan yang di-materialisasi.
SELECT m.definition, p.rolname INTO view_definition, current_owner
FROM pg_matviews m
JOIN pg_class c ON m.matviewname = c.relname AND m.schemaname = target_schema
JOIN pg_roles p ON c.relowner = p.oid
WHERE m.matviewname = view_name
AND m.schemaname = target_schema;
IF view_definition IS NULL THEN
RAISE EXCEPTION 'Gagal mengambil definisi untuk tampilan yang di-materialisasi "%"', view_name;
END IF;
-- Atur nama untuk tampilan baru dan lama.
old_view_name := view_name;
new_view_name := view_name || '_new';
-- Parameter performa untuk IMCI.
SET LOCAL polar_csi.cost_threshold = 0;
-- Cetak rencana kueri.
RAISE NOTICE 'Rencana kueri untuk pembaruan tampilan yang di-materialisasi:';
FOR explain_result IN EXECUTE format('/*+ SET(polar_csi.enable_query on) */ EXPLAIN CREATE MATERIALIZED VIEW %s AS %s', qualified_new_name, view_definition) LOOP
RAISE NOTICE '%', explain_result;
END LOOP;
BEGIN
-- Buat tampilan yang di-materialisasi baru.
EXECUTE format('/*+ SET(polar_csi.enable_query on) */ CREATE MATERIALIZED VIEW %s AS %s', qualified_new_name, view_definition);
-- Jika pemilik baru ditentukan, atur pemilik.
IF new_owner IS NOT NULL THEN
-- Verifikasi bahwa pengguna ada.
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = new_owner) THEN
RAISE EXCEPTION 'Role "%" tidak ada', new_owner;
END IF;
EXECUTE format('ALTER MATERIALIZED VIEW %s OWNER TO %I', qualified_new_name, new_owner);
RAISE NOTICE 'Mengubah pemilik dari "%" ke "%"', current_owner, new_owner;
END IF;
-- Salin semua indeks dari tampilan lama ke tampilan baru.
FOR index_record IN
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = old_view_name
AND schemaname = target_schema
LOOP
-- Ganti nama tampilan lama dengan nama tampilan baru.
index_creation_sql := regexp_replace(
index_record.indexdef,
' ON ' || target_schema || '.' || old_view_name || ' ',
' ON ' || target_schema || '.' || new_view_name || ' ',
'i'
);
-- Tangani kasus khusus indeks UNIQUE.
index_creation_sql := regexp_replace(
index_creation_sql,
'INDEX ' || index_record.indexname || ' ON',
'INDEX ' || index_record.indexname || '_new ON',
'i'
);
RAISE NOTICE 'Membuat indeks: %', index_creation_sql;
EXECUTE index_creation_sql;
END LOOP;
-- Salin izin dari tampilan lama.
RAISE NOTICE 'Memulihkan izin ke tampilan baru %.%', target_schema, new_view_name;
FOR grant_record IN
SELECT
(acl).grantee::regrole::text AS grantee,
(acl).privilege_type
FROM
pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
CROSS JOIN aclexplode(c.relacl) AS acl
WHERE
n.nspname = target_schema
AND c.relname = old_view_name
LOOP
CONTINUE WHEN grant_record.grantee IS NULL;
-- RAISE NOTICE 'Memberikan % ON %.% TO %',
-- grant_record.privilege_type, target_schema, new_view_name, grant_record.grantee;
EXECUTE format(
'GRANT %s ON %I.%I TO %s',
grant_record.privilege_type,
target_schema,
new_view_name,
quote_ident(grant_record.grantee)
);
END LOOP;
-- Hapus tampilan yang di-materialisasi lama.
EXECUTE format('DROP MATERIALIZED VIEW %s', qualified_old_name);
-- Ubah nama tampilan yang di-materialisasi baru menjadi nama aslinya.
EXECUTE format('ALTER MATERIALIZED VIEW %s RENAME TO %I', qualified_new_name, old_view_name);
-- Ubah nama indeks (hapus akhiran _new).
FOR index_record IN
SELECT indexname
FROM pg_indexes
WHERE tablename = old_view_name
AND schemaname = target_schema
LOOP
IF position('_new' in index_record.indexname) > 0 THEN
EXECUTE format(
'ALTER INDEX %I.%I RENAME TO %I',
target_schema,
index_record.indexname,
replace(index_record.indexname, '_new', '')
);
END IF;
END LOOP;
RETURN TRUE;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Gagal memperbarui tampilan yang di-materialisasi: %', SQLERRM;
RETURN FALSE;
END;
END;
$$;Parameter
Parameter | Deskripsi |
| Nama fungsi. Anda dapat mengubahnya sesuai kebutuhan. |
| Nama tampilan yang di-materialisasi. |
| Skema tempat tampilan yang di-materialisasi berada. Default adalah `current_schema`. |
| Pemilik baru dari tampilan yang di-materialisasi yang baru dibuat. |
| Mengizinkan kueri menggunakan indeks penyimpanan kolom. |
| Mengatur ambang batas biaya ke 0 untuk memaksa penggunaan indeks penyimpanan kolom. |
| Mengatur tingkat paralelisme untuk komputasi indeks penyimpanan kolom. Kami merekomendasikan agar nilai ini tidak melebihi jumlah core CPU pada node. |
| Mengatur memori yang tersedia untuk komputasi dalam MB. |
Lakukan pembaruan
Pembaruan manual
Saat diperlukan, Anda dapat secara manual memanggil fungsi untuk melakukan pembaruan. Ganti mv_customer_orders dengan nama tampilan yang di-materialisasi Anda.
SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders');Pembaruan terjadwal menggunakan pg_cron
Anda hanya dapat membuat tugas di database sistem
postgres, dan Anda harus menggunakan akun istimewa.Anda dapat menentukan pemilik untuk tampilan yang di-materialisasi yang dibangun ulang untuk mencegah pengguna biasa tidak dapat membaca tampilan setelah dibuat oleh akun istimewa. Untuk mengubah pengaturan izin lainnya, Anda harus menyesuaikan fungsi pembaruan yang didefinisikan sebelumnya.
Saat Anda menggunakan
pg_cronuntuk pembaruan terjadwal, pastikan interval eksekusi tugas lebih panjang daripada waktu pembaruan aktual tampilan yang di-materialisasi untuk mencegah tugas menumpuk. Karena pembaruan melibatkan penulisan data, biasanya jauh lebih lambat daripada pernyataanSELECTsederhana.
Buat tugas terjadwal
Beralih ke database sistem postgres. Di pg_cron, tentukan parameter seperti nama tugas, interval, dan operasi yang akan dilakukan. Untuk informasi lebih lanjut, lihat ekstensi pg_cron (tugas terjadwal).
Beralih ke database.
\c postgres;Buat tugas terjadwal. Ganti parameter dengan nilai aktual Anda.
CatatanGanti
<mv_name>dengan nama tampilan yang di-materialisasi Anda.Ganti
<database_name>dengan nama sistem bisnis Anda.Ganti
<schema_name>dengan nama skema Anda.Ganti
<user_name>dengan nama pengguna aktual Anda.
Sintaksis
SELECT cron.schedule_in_database( 'refresh_mv_customer_orders', -- Nama tugas (dapat disesuaikan) '*/5 * * * *', -- Ekspresi cron, misalnya jalankan setiap 5 menit $$SELECT refresh_materialized_view_safely_using_csi('<mv_name>', '<schema_name>', '<user_name>')$$, '<database_name>' );Contoh
SELECT cron.schedule_in_database( 'refresh_mv_customer_orders', -- Nama tugas (dapat disesuaikan) '*/5 * * * *', -- Ekspresi cron, misalnya jalankan setiap 5 menit $$SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders', 'public', 'polarpg')$$, 'testdb' );
Lihat tugas terjadwal yang dikonfigurasi
Jalankan pernyataan SQL berikut untuk melihat tugas terjadwal yang telah dikonfigurasi.
SELECT * FROM cron.job;Hasil berikut dikembalikan:
jobid | schedule | command | nodename | nodeport | database | username | active | jobname
-------+-------------+----------------------------------------------------------------------------------------------+----------+----------+----------+----------+--------+----------------------------
1 | */5 * * * * | SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders', 'public', 'polarpg') | /data/. | 3000 | testdb | polarpg | t | refresh_mv_customer_orders
(1 row)Hapus tugas terjadwal
Jika Anda tidak lagi memerlukan pembaruan terjadwal, Anda dapat menjalankan pernyataan SQL berikut untuk menghapus tugas.
SELECT cron.unschedule('refresh_my_materialized_view');Lihat detail eksekusi tugas terjadwal
Anda dapat menjalankan pernyataan SQL berikut untuk melihat detail eksekusi tugas terjadwal.
SELECT * FROM cron.job_run_details;Yang berikut ini dikembalikan:
jobid | runid | job_pid | database | username | command | status | return_message | start_time | end_time
-------+-------+---------+----------+----------+----------------------------------------------------------------------------------------------+-----------+----------------+-------------------------------+-------------------------------
1 | 1 | 76537 | testdb | polarpg | SELECT refresh_materialized_view_safely_using_csi('mv_customer_orders', 'public', 'polarpg') | succeeded | 1 row | 2025-08-27 08:35:00.007231+00 | 2025-08-27 08:35:00.024946+00
(1 rows)Kueri tampilan yang di-materialisasi
Jalankan pernyataan SQL berikut untuk mengkueri tampilan yang di-materialisasi. Ganti mv_customer_orders dengan nama tampilan yang di-materialisasi Anda.
Sebelum Anda menjalankan kueri, pastikan Anda telah beralih ke database bisnis Anda.
SELECT customer_name, COUNT(*) FROM mv_customer_orders GROUP BY customer_name;




