Denormalisasi — Database

Apa itu Denormalisasi? Denormalisasi adalah proses yang berlawanan dengan normalisasi — menambahkan redundansi data secara sengaja untuk meningkatkan performa…

Denormalisasi adalah kebalikan normalisasi: kamu sengaja menyimpan salinan data yang sebenarnya sudah ada di tabel lain, supaya query baca lebih cepat atau lebih sederhana. Bayangin kasir Kedai Senja yang menulis nama pelanggan langsung di setiap nota, supaya gak perlu membuka buku pelanggan tiap kali melihat nota. Cepat. Tapi kalau pelanggan ganti nama, nota-nota lama masih menulis nama yang lama, kecuali ada yang rajin membetulkannya satu per satu.

🧪 Tebakan cepat: nama pelanggan disalin ke kolom orders.customer_name. Siti mengganti namanya jadi "Siti Aminah" di tabel customers. Halaman daftar pesanan membaca orders.customer_name. Nama apa yang tampil di pesanan Siti: "Siti" atau "Siti Aminah"? Tebak dulu, jawabannya ada di bagian "Salinan yang basi".

Desain normal: satu JOIN di setiap halaman

Setelah 3NF, nama pelanggan cuma ada di customers, dan orders menyimpan customer_id:

customers.id name
1 Budi
2 Siti
3 Andi
orders.id customer_id total
1 1 56000
2 1 22000
3 2 33000
4 2 18000
5 3 15000

Halaman daftar pesanan butuh nama pelanggan, jadi query-nya selalu JOIN:

EXPLAIN QUERY PLAN
SELECT o.id, c.name AS customer_name, o.total
FROM orders o
JOIN customers c ON c.id = o.customer_id;
SCAN o
SEARCH c USING INTEGER PRIMARY KEY (rowid=?)

Dua langkah: SQLite membaca semua pesanan, lalu untuk setiap pesanan mencari pelanggannya lewat primary key. Untuk 5 pesanan ini gak terasa. Untuk halaman yang dibuka ribuan kali sehari dengan 5 tabel di-JOIN, itu kerja yang berulang terus.

Denormalisasi: salin kolomnya

ALTER TABLE orders ADD COLUMN customer_name TEXT;

UPDATE orders
SET customer_name = (SELECT name FROM customers WHERE customers.id = orders.customer_id);

EXPLAIN QUERY PLAN
SELECT id, customer_name, total FROM orders;
SCAN orders

Itulah definisinya: menambahkan redundansi data untuk mempercepat query baca. Nama Siti sekarang tersimpan di 3 tempat: 1 baris customers dan 2 baris orders.

Salinan yang basi

UPDATE customers SET name = 'Siti Aminah' WHERE id = 2;

SELECT o.id, c.name AS di_customers, o.customer_name AS di_orders
FROM orders o
JOIN customers c ON c.id = o.customer_id;
id di_customers di_orders
1 Budi Budi
2 Budi Budi
3 Siti Aminah Siti
4 Siti Aminah Siti
5 Andi Andi

Halaman pesanan masih menulis "Siti". Itu jawaban tebakannya. Database gak tahu orders.customer_name adalah salinan, jadi dia gak ikut mengubahnya. Ini risiko utama denormalisasi: data jadi gak konsisten (anomali saat update) karena redundansi. Desain normal gak punya masalah ini, karena di sana nama Siti cuma ada di satu tempat.

Menjaga salinan tetap segar

Setiap salinan butuh penjaga. Di SQLite (juga MySQL dan PostgreSQL, dengan sintaks yang beda) penjaganya bisa trigger: perintah yang otomatis jalan setiap kali sebuah tabel berubah.

CREATE TRIGGER sync_customer_name
AFTER UPDATE OF name ON customers
BEGIN
  UPDATE orders SET customer_name = NEW.name WHERE customer_id = NEW.id;
END;

Sekarang ganti nama Siti menulis 3 baris: 1 di customers dan 2 salinannya di orders. Siti punya 2.000 pesanan? 2.001 baris. Itu harga denormalisasi: baca jadi murah, tulis jadi mahal. Di Laravel, penjaga yang sama sering ditulis sebagai model event atau observer, dan risikonya sama: lupa satu jalur update, salinannya basi.

Teknik denormalisasi lain yang sering dipakai:

Kapan denormalisasi tepat

Normalisasi dulu, denormalisasi belakangan, dan cuma kalau ada bukti: query lambat di slow query log atau EXPLAIN. Denormalisasi tepat kalau performa baca jauh lebih penting daripada konsistensi sempurna, misalnya dashboard, laporan, atau halaman yang dibaca jauh lebih sering daripada ditulis. Latihan pelajaran ini memakai aturan praktis berikut:

Persentase baca Rata-rata JOIN per query Saran
80% ke atas 3 ke atas sangat disarankan
60% ke atas 2 ke atas pertimbangkan
selain itu tidak perlu

Angka ini titik awal diskusi, bukan hukum. Yang menentukan tetap hasil pengukuran.

Dua hal yang bukan alasan denormalisasi:

⚠️ Jebakan yang sering ketemu

TL;DR: Denormalisasi = sengaja menambahkan redundansi data (salinan kolom, counter cache, tabel ringkasan) untuk mempercepat query baca, misalnya orders.customer_name supaya halaman pesanan gak perlu JOIN. Plan-nya turun dari SCAN o + SEARCH c jadi SCAN orders. Risiko utamanya data anomali saat update: Siti ganti nama, salinannya tetap "Siti" sampai dijaga trigger atau kode, dan setiap ganti nama jadi menulis banyak baris. Lakukan setelah normalisasi, kalau ada bukti, dan kalau performa baca lebih penting daripada konsistensi sempurna.

Yang akan kamu pelajari