Kamu lagi lihat halaman detail produk di toko online-mu sendiri, lalu penasaran: “Wah, iPhone 15 ini sudah terjual berapa, ya?”
Satu pertanyaan sederhana—tapi di balik layar, prosesnya bisa jadi mimpi buruk. Tiap kali halaman itu diakses, database-mu sibuk menghitung seluruh riwayat transaksi. Begitu data order membengkak, angka “Terjual” tadi justru bikin loading ikut membengkak.
Kalau kamu sedang menghadapi agregat function yang melambat, tulisan ini akan memberikan tiga solusi teknis dari pengalaman langsung—termasuk satu kesalahan klasik yang hampir semua developer lakukan di awal.
Kenapa Agregat Function Tetap Lambat Meski Sudah Kamu Pasang Index?
Ini bagian yang paling sering bikin frustasi. Anggapan umum: “Tambahkan index di kolom product_id, maka COUNT atau SUM akan ngebut.”
Sayangnya, tidak sesederhana itu.
Paradoks Agregat: Index Membantu Pencarian, Bukan Penghitungan Massal
Saat kamu membuat index, database menggunakan struktur B-Tree untuk menemukan baris yang relevan dengan cepat. Jika query-mu hanya mengambil sebagian data (misal LIMIT 10), index bekerja sempurna.
Tapi, fungsi agregat seperti COUNT, MIN, MAX, atau AVG punya karakter berbeda. Mereka perlu menghitung semua baris yang lolos filter.
Contoh nyata: kamu ingin tahu total order item untuk produk iPhone 15 (product_id = 'xxx'). Query-nya sederhana:
SELECT COUNT(id) FROM order_items WHERE product_id = 'xxx';
Walau index di product_id bisa melompat langsung ke blok data yang sesuai, hasil pencariannya tetap bisa berjumlah puluhan ribu—bahkan jutaan baris jika produk itu laris.
Di sinilah masalah muncul: database tidak menyimpan hasil akhir agregasi di dalam index. Ia tetap harus men-scan seluruh baris hasil filter satu per satu untuk menghitung jumlahnya. Semakin banyak baris hasil filter, semakin lama proses COUNT-nya.
Hal yang sama berlaku untuk MIN atau MAX: database memindai seluruh baris untuk membandingkan nilai, bukan sekadar mengambil satu record dari index.
Counter-intuitive insight: Index tidak mempercepat agregasi pada dataset besar; ia hanya mempersempit wilayah scanning. Bagian “menghitung total” tetap jalan manual, dan di sinilah letak bottleneck-nya.
Jadi, kalau datamu masih ratusan atau ribuan baris saja, agregat function masih aman. Begitu menembus puluhan ribu apalagi jutaan, responnya bakal merangkak turun drastis.
Lantas, apa jalan keluarnya?
Tiga Solusi Jitu Mengatasi Agregat Function Lambat (Tanpa Ganti Database)
Ketiga solusi ini saya gunakan di production—termasuk di sistem e-commerce dengan volume order tinggi. Pilih mana yang paling sesuai dengan konteksmu.
1. Ganti Real-Time Agregat dengan Summary Table + Async Update
Konsep: Hitung data agregasi di belakang layar, bukan saat halaman diakses. Hasilnya disimpan di tabel khusus, lalu halaman depan tinggal baca dari tabel itu.
Bagaimana Cara Kerjanya?
- Buat tabel product_summary dengan kolom seperti:
product_id,total_sold,total_revenue, dll. - Setiap kali terjadi order baru, jangan hitung ulang agregat di transaksi yang sama. Prosesnya dipecah: transaksi order tetap simpan data mentah, lalu secara asinkronus (via queue/job worker) kamu hitung ulang total produk yang relevan dan update tabel summary.
- Saat halaman detail produk diakses, cukup
SELECT * FROM product_summary WHERE product_id = 'xxx'. Tidak ada lagi aggregasi join besar ke tabel order.
Kelebihan:
- Request halaman jadi sangat ringan, hampir setara membaca satu baris data.
- Bisa diimplementasikan di database apa pun.
Kekurangan:
- Data tidak benar-benar real-time. Ada jeda beberapa detik hingga menit tergantung kecepatan job worker.
- Jika dalam satu menit ada ratusan order, job worker bisa keteteran. Namun tetap lebih ringan ketimbang memukul agregasi setiap request.
Kapan menggunakannya?
Ketika kamu butuh data yang cukup fresh tapi tidak harus up-to-the-second. Cocok untuk sebagian besar dashboard internal dan halaman produk.
2. Batch Processing: Hitung Sekali, Simpan untuk Periode Tertentu
Banyak toko online besar yang tidak repot-repot menghitung “terjual 1.234 item” secara real time. Mereka melakukannya secara batch—misalnya setiap 1 jam, 6 jam, atau bahkan 1 hari sekali.
Arsitektur yang Umum Digunakan:
- Data transaksi tetap ditulis di database utama (misal PostgreSQL).
- Secara periodik, data tersebut dikirim ke data warehouse (seperti Google BigQuery, ClickHouse, atau Apache Spark).
- Di data warehouse inilah proses agregation berat dijalankan. Hasilnya: jumlah total barang terjual, rating produk, jumlah follower seller, dsb.
- Hasil olahan itu kemudian dikembalikan ke tabel summary di database aplikasi—sekali push per periode.
Kenapa pendekatan ini populer?
- Agregasi dilakukan di sistem yang memang dioptimasi untuk query analitik, bukan di database transaksional yang sibuk.
- Beban puncak pada database utama tidak bertambah; pekerjaan “berat” dilimpahkan ke tim data.
- Developer aplikasi hanya menyediakan tempat penyimpanan hasil, tidak lagi pusing membuat kalkulasi manual.
Kelemahan:
- Ada delay sesuai interval batch. Produk yang tadi pagi laris manis bisa jadi belum mencerminkan angka terbaru sampai batch berikutnya.
- Infrastruktur lebih kompleks; butuh pipeline data.
Kapan menggunakannya?
Ketika skala bisnis sudah besar, volume order sangat tinggi, dan kamu punya data team sendiri. Ini solusi paling scalable.
3. Materialized View: Otomatis Menyimpan Hasil Agregasi (Jika Database Support)
Jika kamu beruntung menggunakan PostgreSQL, Oracle, atau database lain yang mendukung Materialized View, ada jalan pintas canggih.
Bedanya View Biasa vs Materialized View
| View Biasa | Materialized View |
|---|---|
| Hanya menyimpan definisi query. Tiap kali diakses, query dijalankan lagi secara real time. | Menyimpan hasil query secara fisik—seperti snapshot. Saat diakses, data diambil langsung dari snapshot tanpa menjalankan ulang query sumber. |
| Cocok untuk data yang selalu berubah dan ringan. | Cocok untuk agregasi berat yang jarang berubah tapi sering dibaca. |
Bagaimana Materialized View Membantu?
Kamu bisa membuat Materialized View yang mendefinisikan agregasi yang dibutuhkan, misalnya:
CREATE MATERIALIZED VIEW product_summary AS SELECT product_id, COUNT(*) as total_sold FROM order_items GROUP BY product_id;
Setelah itu, setiap kali kamu butuh data total terjual, tinggal query SELECT * FROM product_summary WHERE product_id = 'xxx'. Sumber data tidak lagi tersentuh.
Lantas, kapan snapshot-nya diperbarui?
Kamu bisa mengatur tiga cara pembaruan:
- Manual: Kamu refresh secara berkala via cron atau job.
- Trigger-based: Beberapa database (seperti PostgreSQL dengan extension) bisa mendeteksi perubahan di tabel sumber dan merefresh otomatis—ini mirip solusi pertama, tapi dijalankan oleh sistem database, bukan aplikasi.
- Incremental Refresh: Hanya memproses data yang berubah, tersedia di beberapa DBMS kelas enterprise.
Kelebihan:
- Implementasi lebih bersih; tidak perlu menulis kode aplikasi summary manual.
- Pembaruan bisa dikonfigurasi sesuai kebutuhan.
Kekurangan:
- Tidak semua database mendukung (MySQL standar tidak punya fitur ini; MariaDB/MySQL butuh plugin atau workaround).
- Jika frekuensi pembaruan terlalu sering dan data sangat besar, resource tetap bisa terbebani.
Kapan menggunakannya?
Jika kamu memakai PostgreSQL dan ingin solusi semi-real-time yang otomatis tanpa ribet menulis job worker. Cocok untuk tim kecil yang ingin hasil cepat.
Rangkuman: Solusi Mana yang Tepat untuk Kamu?
| Kondisi | Solusi yang Disarankan |
|---|---|
| Data order masih bisa dihitung real time (ribuan), tapi mulai lambat; ingin tanpa delay lama. | Summary Table + Async (#1) |
| Volume order sangat tinggi, ada tim data sendiri, real time bukan prioritas. | Batch Processing via data warehouse (#2) |
| Pakai PostgreSQL, ingin update otomatis tanpa ribet manage job sendiri. | Materialized View (#3) |
| Database tidak mendukung materialized view, order banyak tiap menit. | Kombinasi #1 dan #2: async summary dengan interval batch. |
Jangan Biarkan Query “Sederhana” Melumpuhkan Aplikasi E-Commerce Kamu
Banyak developer terjebak: apa yang terasa simpel di tahap development (tinggal COUNT(*)) bisa menjadi sumber utama bottleneck saat aplikasi ramai pengunjung.
Kamu sekarang sudah paham bahwa index saja tidak cukup—dan punya tiga kerangka solusi untuk menangani agregasi berat tanpa mengorbankan pengalaman pengguna.
Pertanyaan reflektif buatmu: Apakah di aplikasimu saat ini ada satu halaman yang diam-diam mengirim puluhan agregasi real-time? Coba cek, mungkin itulah biang keladi lambatnya performa.
Jika tulisan ini membantumu melihat masalah database dari sudut baru, bagikan ke tim engineering atau diskusikan di kolom komentar—metode mana yang paling cocok untuk use case kamu saat ini?
FAQ: Pertanyaan yang Sering Muncul tentang Agregat Function Lambat
Tidak. Index mempercepat pencarian baris, tapi saat hasilnya jutaan, COUNT tetap akan menghitung manual. Baca penjelasan lengkapnya di bagian “Paradoks Agregat”.
Ya, ada jeda. Kamu bisa memilih pendekatan async (delay detik/menit) atau batch (jam/hari) sesuai toleransi bisnis.
Tergantung. Jika database mendukung dan kamu ingin minim kode, materialized view unggul. Tapi summary table manual memberi kendali penuh dan bisa dibangun pakai database apapun.
Di banyak marketplace, perbedaan data kemarin dan hari ini masih wajar. Angka di etalase seringkali hasil batch harian, bukan detik ini.
MySQL default tidak punya fitur tersebut. Kamu bisa mensimulasikannya dengan tabel summary biasa dan event scheduler.
