Optimasi Database Odoo dengan PostgreSQL Partitioning — 320 GB Jadi 7 Partisi

Pendahuluan
Beberapa minggu yang lalu, saya menghadapi masalah klasik di proyek Odoo 12 klien: database membengkak hingga 320 GB untuk hanya 6 tabel. Ya, 6 tabel saja.
Akar masalahnya? Tabel-tabel ini menyimpan gambar desain produk (binary) yang di-upload untuk setiap proses produksi. Akumulasi bertahun-tahun.
Artikel ini menceritakan pengalaman saya melakukan PostgreSQL partitioning untuk menyelesaikan masalah ini. Untuk pemula yang ingin belajar partitioning, atau sedang mencari solusi untuk database besar.
Apa Itu Partitioning?
Bayangkan lemari arsip kantoran:
- Tanpa partitioning: Semua dokumen dari 2010 sampai 2026 ditumpuk jadi satu tumpukan. Mau cari dokumen 2025? Ngobrak-abrik semua.
- Dengan partitioning: Tumpukan dipecah per tahun. Masing-masing rak sendiri.
Di PostgreSQL:
|
|
Data tetap bisa di-query seperti biasa. Tapi PostgreSQL otomatis hanya membaca partisi yang relevan.
Timeline Proyek
Kondisi awal database:
| Tabel | Ukuran | Isi |
|---|---|---|
tbl_or_line_doc |
241 GB | Gambar desain PO Garmen |
tbl_sample_line_doc |
22 GB | Gambar sample |
tbl_request_line_doc |
19 GB | Gambar request marketing |
tbl_document_po_garmen_line |
18 GB | Dokumen PO |
tbl_approval_desain |
10 GB | Gambar approval desain |
mail_message |
10 GB | Email internal Odoo |
| Total | 320 GB | Hanya 6 tabel! |
Persiapan
Analisa Tabel
Semua tabel memiliki pola yang sama:
|
|
Kolom doc (bytea/binary) berisi gambar desain 2-4 MB per baris. Kali 100 ribu baris = 241 GB.
Pilih Partition Key
Pilihan paling masuk akal: create_date. Alasan:
- Data historis jarang diubah — cocok range partitioning
- Query Odoo sering filter tanggal
- Partisi per tahun — pas granularitasnya
Rencana Partisi
Setiap tabel dipecah jadi 7-8 partisi:
|
|
Script Partitioning
Saya menyusun 12 file SQL:
|
|
Alur Partitioning
Setiap script 01-06 mengikuti pola yang sama:
|
|
Kenapa Metode Copy + Swap?
Karena tabel existing sudah punya data dan index. Dengan copy + swap:
- Copy data di background
- Verifikasi row count
- Swap dalam 1 transaksi (< 1 detik downtime)
- Jika gagal, rollback — data aman
Eksekusi
Persiapan
- Stop Odoo — tidak boleh ada koneksi lain
- Cek free disk — perlu duplikasi. 241 GB → butuh ~300 GB free
- Gunakan screen — proses berjam-jam
|
|
00_preparation.sql — Persiapan
Berjalan mulus — membuat tabel log, backup FK, cek NULL. Semua 0 NULL.
01_tbl_or_line_doc.sql — 241 GB
Ini yang terberat. 7,5 jam kemudian:
|
|
7,5 jam hanya untuk copy data. Kenapa? Setiap baris berisi binary 2-4 MB. PostgreSQL harus baca, tulis, kompres TOAST, tulis WAL.
Yang Tidak Berjalan Mulus
Koneksi SSH terputus di tengah copy! Saya belum pakai screen saat itu. PostgreSQL otomatis rollback — data aman. Tapi 241 GB harus di-copy ulang.
Pelajaran: Screen itu WAJIB.
|
|
Analisa I/O Disk
Dari metrik PostgreSQL:
|
|
|
|
Throughput hanya 12,5 MB/s — idealnya 80-160 MB/s untuk HDD. Server mengalami I/O bottleneck. Tapi partitioning tetap berhasil.
Hasil
Sebelum Partitioning
|
|
Sesudah Partitioning
|
|
Verifikasi
|
|
Yang Saya Pelajari
1. Data Binary Sangat Berat
241 GB yang saya pikir adalah “baris” ternyata 99% adalah binary di TOAST storage. Operasi copy TOAST jauh lebih lambat.
2. I/O Disk adalah Raja
Untuk database besar, SSD bukan pilihan — keharusan. HDD dengan 12,5 MB/s membuat copy 241 GB butuh 7,5 jam.
3. Screen Itu Wajib
Satu perintah screen -S partitioning menyelamatkan dari frustrasi koneksi putus.
4. Script Idempotent = Tenang
Dengan DROP TABLE IF EXISTS, script bisa dijalankan ulang kapan saja tanpa error.
Automation
Untuk maintenance ke depan:
|
|
Fungsi ini otomatis:
- Deteksi tahun terakhir dari partisi yang ada
- Drop partisi
_p_future(catch-all) - Buat partisi baru
- Re-create
_p_futuredengan batas baru
Kesimpulan
Apa yang saya dapatkan:
| Sebelum | Sesudah |
|---|---|
| 1 tabel 241 GB | 7 partisi 0-83 GB |
| Query FULL SCAN | Query partition pruning |
| VACUUM lock 241 GB | VACUUM per partisi kecil |
| Backup semua data | Backup per tahun |
Apa yang saya korbankan:
- 7,5 jam waktu copy
- ~500 GB IOPs selama proses
Apakah sepadan? Sangat. Untuk database yang terus bertambah, partitioning adalah investasi jangka panjang.