Membangun dashboard analitik yang memperlihatkan angka penjualan, status server, atau transaksi secara real-time adalah fitur standar di era aplikasi SaaS modern. Pertanyaannya, bagaimana cara Anda mengambil data terbaru dari database?
Banyak developer pemula terjebak dengan teknik Polling. Mereka menulis query SELECT COUNT(*) FROM transactions di backend, dan meminta frontend (React/Vue) untuk melakukan fetch setiap 2 detik.
Apa yang terjadi? Jika ada 100 pengguna membuka dashboard tersebut, database Anda akan dihujani 50 query analitik berat setiap detiknya. CPU server MySQL akan melonjak 100%, sistem transaksi utama macet, dan aplikasi Anda akan melambat.
Di level enterprise, masalah ini dipecahkan dengan menggunakan pattern Change Data Capture (CDC). Mari kita pelajari bagaimana cara kerjanya.
Daftar Isi
- Apa itu Change Data Capture (CDC)?
- Arsitektur Sistem Real-Time
- Konfigurasi MySQL (Mengaktifkan Binlog)
- Backend: Membaca Binlog & Emit SSE (Node.js)
- Frontend: Dashboard Analitik (React)
- Kesimpulan & Skala Enterprise (Debezium)
1. Apa itu Change Data Capture (CDC)?
Daripada terus-menerus bertanya kepada database "Apakah ada data baru?", CDC membalik logikanya: Biarkan database yang memberi tahu kita saat ada data yang berubah.
MySQL menyimpan seluruh riwayat perubahan datanya (INSERT, UPDATE, DELETE) ke dalam sebuah file yang disebut Binary Log (binlog). File ini sebenarnya dirancang agar Slave database bisa menyalin data dari Master. Namun, dengan trik CDC, kita membuat aplikasi Node.js kita "menyamar" sebagai Slave sehingga ia akan langsung dikirimi event data baru sedetik setelah data tersebut masuk ke tabel.
2. Arsitektur Sistem Real-Time
Berikut adalah alur data arsitektur CDC yang efisien dan tidak membebani query SELECT sama sekali.

- Master DB (MySQL): Menerima transaksi biasa dari aplikasi utama. Setiap transaksi otomatis dicatat di binlog.
- CDC Service (Node.js): Layanan mikro terpisah yang terkoneksi ke port MySQL dan membaca binlog stream secara kontinu.
- Server-Sent Events (SSE): Node.js mem- parsing data biner tersebut menjadi JSON, lalu mendorongnya (Push) melalui kanal koneksi HTTP terbuka satu arah (SSE) ke web browser pengguna.
- React Dashboard: Tampilan web yang merender ulang chart atau tabel tanpa perlu me- refresh halaman.
3. Konfigurasi MySQL (Mengaktifkan Binlog)
Agar CDC bisa membaca baris data yang diubah, format binlog harus diatur ke ROW. Edit file konfigurasi MySQL Anda (my.cnf atau mysqld.cnf):
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
Restart layanan MySQL Anda. Buat juga satu user dengan hak akses REPLICATION:
CREATE USER 'cdc_user'@'%' IDENTIFIED BY 'rahasia123';
GRANT REPLICATION SLAVE, REPLICATION CLIENT, SELECT ON *.* TO 'cdc_user'@'%';
FLUSH PRIVILEGES;
4. Backend: Membaca Binlog & Emit SSE (Node.js)
Kita akan menggunakan pustaka zongji di Node.js, sebuah pustaka berbasis mysqljs yang dirancang khusus untuk menangkap binlog.
Instalasi:
npm install zongji express cors
Buat file server.js:
const express = require('express');
const cors = require('cors');
const ZongJi = require('zongji');
const app = express();
app.use(cors());
// Simpan klien SSE yang sedang aktif
let clients = [];
// 1. Endpoint Server-Sent Events (SSE)
app.get('/api/stream/transactions', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Kirim header awal agar koneksi terjaga
res.write(': connected\n\n');
clients.push(res);
req.on('close', () => {
clients = clients.filter(client => client !== res);
});
});
// Fungsi untuk mengirim data ke semua klien terhubung
const sendToClients = (data) => {
clients.forEach(client => client.write(`data: ${JSON.stringify(data)}\n\n`));
};
// 2. Setup ZongJi (MySQL Binlog Reader)
const zongji = new ZongJi({
host: 'localhost',
user: 'cdc_user',
password: 'rahasia123',
serverId: 99, // Harus unik
});
// Tangkap event Binlog
zongji.on('binlog', function(evt) {
if (evt.getTypeName() === 'WriteRows') {
const tableName = evt.tableMap[evt.tableId].tableName;
// Hanya pedulikan tabel 'transactions'
if (tableName === 'transactions') {
evt.rows.forEach(row => {
const newData = {
event: 'INSERT',
id: row.transaction_id,
amount: row.amount,
status: row.status,
timestamp: new Date()
};
// Langsung PUSH ke React Dashboard!
sendToClients(newData);
});
}
}
});
zongji.start({
includeEvents: ['tablemap', 'writerows', 'updaterows']
});
app.listen(3001, () => {
console.log('CDC Service berjalan di port 3001');
});
Kode di atas memantau tabel transactions. Tanpa melakukan query SELECT, setiap kali ada aplikasi lain yang melakukan INSERT INTO transactions ..., Node.js akan tahu dan langsung meneruskannya ke semua klien SSE yang sedang membuka dashboard.
5. Frontend: Dashboard Analitik (React)
Di sisi antarmuka, Server-Sent Events (SSE) didukung secara native oleh web browser modern melalui objek EventSource. Ini lebih ringan dari WebSocket karena hanya berupa jalur download sepihak dari server ke client (sempurna untuk analitik).
Berikut adalah rancangan UI Dashboard untuk analitik real-time.

import React, { useState, useEffect } from 'react';
export default function AnalyticsDashboard() {
const [transactions, setTransactions] = useState([]);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
// Membuka koneksi SSE ke server Node.js
const eventSource = new EventSource('http://localhost:3001/api/stream/transactions');
eventSource.onopen = () => setIsConnected(true);
eventSource.onmessage = (event) => {
const newTx = JSON.parse(event.data);
// Update state dengan data terbaru di urutan paling atas,
// simpan maksimal 10 data terakhir agar memori browser aman
setTransactions((prev) => [newTx, ...prev].slice(0, 10));
};
eventSource.onerror = () => {
setIsConnected(false);
eventSource.close();
};
return () => eventSource.close();
}, []);
return (
<div className="p-8 font-sans bg-gray-50 min-h-screen">
<div className="flex justify-between items-center mb-8 border-b pb-4">
<h1 className="text-2xl font-bold tracking-tight">ANALYTICS // DASHBOARD</h1>
<div className="flex items-center space-x-2">
{/* Indikator "Live" berkedip */}
<span className={`h-3 w-3 rounded-full ${isConnected ? 'bg-green-500 animate-pulse' : 'bg-red-500'}`}></span>
<span className="text-sm text-gray-600 font-mono">
{isConnected ? 'LIVE CONNECTION (SSE)' : 'DISCONNECTED'}
</span>
</div>
</div>
<div className="bg-white border border-gray-200 shadow-sm rounded-md">
<div className="bg-gray-100 px-4 py-2 border-b border-gray-200 font-semibold text-gray-700 text-sm">
LATEST TRANSACTIONS
</div>
<table className="w-full text-left text-sm">
<thead className="bg-white font-medium text-gray-500">
<tr>
<th className="px-4 py-2">TIMESTAMP</th>
<th className="px-4 py-2">ID</th>
<th className="px-4 py-2">AMOUNT (USD)</th>
<th className="px-4 py-2">STATUS</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-100">
{transactions.map((tx, idx) => (
<tr key={idx} className="hover:bg-blue-50 transition-colors duration-300">
<td className="px-4 py-3 font-mono text-xs">{new Date(tx.timestamp).toLocaleTimeString()}</td>
<td className="px-4 py-3">{tx.id}</td>
<td className="px-4 py-3 font-medium">${tx.amount}</td>
<td className="px-4 py-3 text-green-600 font-semibold">{tx.status}</td>
</tr>
))}
{transactions.length === 0 && (
<tr>
<td colSpan={4} className="px-4 py-8 text-center text-gray-400">Menunggu transaksi masuk...</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
);
}
6. Kesimpulan & Skala Enterprise (Debezium)
Menggunakan CDC (zongji + Node.js) membuat beban query analitik ke MySQL turun hingga 0% karena kita hanya "mendengarkan" aktivitas disk.
Namun, jika aplikasi Anda berkembang ke skala enterprise (ribuan operasi write per detik), pustaka native Node.js ini berisiko kehabisan memori. Di sinilah Apache Kafka dan Debezium masuk.
Debezium adalah konektor CDC standar industri berbasis Java. Ia membaca binlog MySQL, mengubahnya menjadi format seragam, lalu melemparnya ke antrean Apache Kafka. Setelah data masuk Kafka, ratusan microservice analitik atau layanan machine learning bisa mengonsumsi data tersebut secara serentak, aman, dan tanpa pernah sekalipun menyentuh basis data produksi Anda!