Kodeloka
  1. Kodeloka
  2. Kelas PostgreSQL
  3. Materi

8 · Praktik · Modul 10 dari 12

Migrasi Skema Tanpa Downtime

Perubahan skema mana yang aman, mana yang mengunci tabel, dan pola expand-contract untuk mengubah struktur tanpa menghentikan layanan.

Perintah DDL dan Dampak Penguncian

OperasiAman?Catatan
ADD COLUMN tanpa default✅ InstanHanya mengubah katalog
ADD COLUMN ... DEFAULT✅ InstanSejak PostgreSQL 11
DROP COLUMN✅ InstanRuang kembali setelah VACUUM
ALTER TYPE (perbesar varchar)✅ InstanHanya bila memperbesar
ALTER TYPE (ubah tipe)❌ Menulis ulangMengunci tabel penuh
SET NOT NULL⚠️ Memindai penuhBisa dipercepat, lihat di bawah
ADD FOREIGN KEY⚠️ MemindaiPakai NOT VALID
CREATE INDEX❌ Mengunci tulisPakai CONCURRENTLY
🚫 Bahaya antrean kunci

Ini jebakan yang sangat merusak dan sering tidak disadari. Bahkan DDL yang biasanya instan pun harus menunggu kunci. Bila ada kueri panjang yang sedang berjalan, DDL Anda mengantre — dan semua kueri baru mengantre di belakangnya, termasuk SELECT sederhana. Satu ALTER TABLE yang tampak sepele bisa menghentikan seluruh aplikasi.

-- SELALU pasang lock_timeout sebelum DDL
SET lock_timeout = '3s';
ALTER TABLE pesanan ADD COLUMN catatan text;
-- Bila tidak bisa mendapat kunci dalam 3 detik, gagal saja
-- lalu coba lagi nanti — jauh lebih baik daripada memblokir semuanya.

Menambah Constraint dengan Aman

-- ❌ Memindai seluruh tabel sambil mengunci
ALTER TABLE pesanan ADD CONSTRAINT ck_total CHECK (total >= 0);

-- ✅ Dua langkah
ALTER TABLE pesanan ADD CONSTRAINT ck_total
    CHECK (total >= 0) NOT VALID;          -- instan, berlaku untuk data BARU
ALTER TABLE pesanan VALIDATE CONSTRAINT ck_total;  -- pindai tanpa mengunci tulis
-- SET NOT NULL tanpa pemindaian panjang (PostgreSQL 12+)
ALTER TABLE pesanan ADD CONSTRAINT ck_notnull
    CHECK (pengguna_id IS NOT NULL) NOT VALID;
ALTER TABLE pesanan VALIDATE CONSTRAINT ck_notnull;
ALTER TABLE pesanan ALTER COLUMN pengguna_id SET NOT NULL;  -- instan
ALTER TABLE pesanan DROP CONSTRAINT ck_notnull;

Pola Expand-Contract

Tujuan: mengganti nama kolom "nama" menjadi "nama_lengkap"

RILIS 1 — EXPAND
  ALTER TABLE pengguna ADD COLUMN nama_lengkap text;
  CREATE TRIGGER sinkron ...   -- tulis ke keduanya
  Aplikasi: menulis ke KEDUANYA, membaca dari "nama"

RILIS 2 — ISI DATA
  UPDATE pengguna SET nama_lengkap = nama
  WHERE nama_lengkap IS NULL;    -- dalam potongan kecil!

RILIS 3 — PINDAH BACA
  Aplikasi: menulis ke keduanya, membaca dari "nama_lengkap"

RILIS 4 — CONTRACT
  Aplikasi: hanya memakai "nama_lengkap"
  DROP TRIGGER sinkron;
  ALTER TABLE pengguna DROP COLUMN nama;
💡 Kenapa harus serumit ini

Selama rilis bergulir, versi lama dan baru aplikasi berjalan bersamaan terhadap basis data yang sama. Mengganti nama kolom dalam satu langkah akan langsung merusak versi lama yang masih melayani trafik. Aturan yang bisa dipegang: setiap perubahan skema harus kompatibel dengan versi aplikasi sebelumnya dan sesudahnya.

Memperbarui Data dalam Jumlah Besar

🚫 Jangan UPDATE jutaan baris sekaligus
-- ✅ Potong-potong dengan jeda
DO $$
DECLARE
    terpengaruh int;
BEGIN
    LOOP
        UPDATE pesanan SET wilayah = 'jakarta'
        WHERE id IN (
            SELECT id FROM pesanan
            WHERE wilayah IS NULL
            LIMIT 5000
            FOR UPDATE SKIP LOCKED
        );
        GET DIAGNOSTICS terpengaruh = ROW_COUNT;
        EXIT WHEN terpengaruh = 0;
        COMMIT;
        PERFORM pg_sleep(0.1);   -- beri ruang untuk autovacuum & replika
    END LOOP;
END $$;

Daftar Periksa Migrasi

PeriksaKenapa
lock_timeout disetelMencegah antrean kunci melumpuhkan aplikasi
Setiap migrasi punya prosedur mundurRilis bisa dibatalkan
Diuji pada salinan data produksiDurasi pada 10 baris tidak mewakili 10 juta baris
Indeks dibuat CONCURRENTLYTidak memblokir penulisan
Pembaruan data dipecahMenjaga WAL, kunci, dan lag replika tetap terkendali
Kompatibel dengan versi aplikasi sebelumnyaRilis bergulir menjalankan dua versi bersamaan
📌 Perkakas yang membantu

Alat migrasi seperti Flyway, Liquibase, golang-migrate, atau Alembic menangani pengurutan dan pencatatan versi. Untuk menangkap DDL berbahaya sebelum sampai produksi, squawk dapat dijalankan di pipeline CI untuk memeriksa berkas migrasi Anda dan memperingatkan operasi yang mengunci.