Partisi Tabel PostgreSQL — Ketika Tabel Anda Mencapai 10 Juta+ Baris
Engineering Team
Kapan Mempartisi
Partisi tabel adalah teknik membagi satu tabel logis menjadi beberapa tabel fisik yang lebih kecil. PostgreSQL mengelolanya secara transparan — query terhadap tabel utama secara otomatis diarahkan ke partisi yang relevan.
Anda perlu mempertimbangkan partisi ketika:
- Tabel memiliki lebih dari 10 juta baris
- Query WHERE selalu menyertakan kolom tertentu (tanggal, locale, tenant)
- VACUUM memakan waktu terlalu lama
- Anda perlu menghapus data lama secara efisien
Jenis Partisi
1. Range Partitioning
Paling umum. Membagi berdasarkan rentang nilai — biasanya tanggal:
CREATE TABLE events (
id UUID DEFAULT gen_random_uuid(),
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (created_at);
-- Partisi per bulan
CREATE TABLE events_2024_01 PARTITION OF events
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE events_2024_02 PARTITION OF events
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
CREATE TABLE events_2024_03 PARTITION OF events
FOR VALUES FROM ('2024-03-01') TO ('2024-04-01');
2. List Partitioning
Membagi berdasarkan nilai diskrit — cocok untuk multi-tenant atau multi-locale:
CREATE TABLE articles (
id UUID DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
content TEXT NOT NULL,
locale TEXT NOT NULL,
published_at TIMESTAMPTZ
) PARTITION BY LIST (locale);
CREATE TABLE articles_en PARTITION OF articles FOR VALUES IN ('en');
CREATE TABLE articles_id PARTITION OF articles FOR VALUES IN ('id');
CREATE TABLE articles_ru PARTITION OF articles FOR VALUES IN ('ru');
CREATE TABLE articles_other PARTITION OF articles DEFAULT;
3. Hash Partitioning
Mendistribusikan baris secara merata — cocok ketika tidak ada kolom range/list yang jelas:
CREATE TABLE user_sessions (
id UUID DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
data JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
) PARTITION BY HASH (user_id);
CREATE TABLE user_sessions_0 PARTITION OF user_sessions
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE user_sessions_1 PARTITION OF user_sessions
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE user_sessions_2 PARTITION OF user_sessions
FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE user_sessions_3 PARTITION OF user_sessions
FOR VALUES WITH (MODULUS 4, REMAINDER 3);
Migrasi dari Tabel Non-Partisi
-- 1. Rename tabel asli
ALTER TABLE events RENAME TO events_old;
-- 2. Buat tabel partisi baru
CREATE TABLE events (...) PARTITION BY RANGE (created_at);
-- 3. Buat partisi
CREATE TABLE events_2024_01 PARTITION OF events ...;
-- 4. Migrasikan data
INSERT INTO events SELECT * FROM events_old;
-- 5. Verifikasi dan hapus tabel lama
DROP TABLE events_old;
Partition Pruning
Partition pruning adalah optimasi di mana PostgreSQL hanya memindai partisi yang relevan:
-- Hanya memindai events_2024_01
EXPLAIN SELECT * FROM events WHERE created_at = '2024-01-15';
-- Output:
-- Append
-- -> Seq Scan on events_2024_01
-- Filter: (created_at = '2024-01-15')
Tanpa partisi, query akan memindai seluruh tabel. Dengan partisi, PostgreSQL melewatkan partisi yang tidak relevan.
Dampak pada Performa
| Operasi | Tanpa Partisi | Dengan Partisi |
|---|---|---|
| SELECT WHERE date | Full scan 100M rows | Scan 1 partisi (~3M rows) |
| DELETE data lama | Slow, generates WAL | DROP TABLE, instant |
| VACUUM | Locks 100M row table | Vacuum per partisi |
| Index rebuild | Hours | Minutes per partisi |
Otomatisasi Partisi
-- Buat partisi secara otomatis dengan pg_partman
CREATE EXTENSION pg_partman;
SELECT partman.create_parent(
p_parent_table => 'public.events',
p_control => 'created_at',
p_type => 'native',
p_interval => '1 month',
p_premake => 3 -- Buat 3 partisi ke depan
);
Kesimpulan
Partisi tabel PostgreSQL adalah teknik kritis ketika data tumbuh melewati 10 juta baris. Range partitioning untuk data time-series, list untuk multi-tenant/locale, dan hash untuk distribusi merata. Partition pruning memastikan query hanya menyentuh data yang relevan, dan maintenance (VACUUM, reindex) menjadi jauh lebih cepat.