# Optimasi Performa Query MariaDB untuk Trafo Skala Juta Pengguna

> Diterbitkan oleh Literally Web ID pada 21 Oct 2026
> Sumber: https://literally.web.id/articles/optimasi-performa-query-mariadb-untuk-trafo-skala-juta-pengguna

---

# Optimasi Performa Query MariaDB untuk Trafo Skala Juta Pengguna

## 1. Titik Awal Masalah: Mengapa Arsitektur Lama Sering Ambruk di Produksi

Pukul 03:14 pagi. Ponsel saya bergetar keras, membangunkan saya dari tidur nyenyak. PagerDuty berteriak histeris: *High CPU Utilization on Database Cluster* dan *HTTP 504 Gateway Timeouts* melonjak tajam. Dashboard Grafana menyala merah menyala, menunjukkan grafik koneksi MariaDB menembus batas maksimal, sementara Nginx di front-end megap-megap menunggu respons dari PHP-FPM yang tersangkut di antrean database.

Sistem kami—sebuah platform IoT untuk pemantauan trafo listrik cerdas yang melayani jutaan pengguna aktif harian—tiba-tiba lumpuh. Ribuan perangkat keras di lapangan terus-menerus mengirimkan telemetri setiap detiknya. Di balik layar, query SQL yang awalnya dirasa "cukup aman" saat testing lokal dengan ribuan data, kini berubah menjadi monster penghonsumsi sumber daya begitu berhadapan dengan tabel yang berisi ratusan juta baris data histori.

> [!WARNING]
> Jangan pernah berasumsi query yang berjalan mulus di lingkungan staging akan berperilaku sama ketika tabel produksi Anda telah membengkak melampaui 10 juta baris.

Masalah utamanya bukan pada kurangnya kapasitas server, melainkan cara kita memperlakukan data. Query tanpa indeks yang tepat, konfigurasi buffer pool default, dan buruknya manajemen koneksi membuat database kita bekerja bagaikan kuli panggul yang mencoba mengangkat kontainer sendirian. Mari kita bedah bagaimana cara mengubah kekacauan ini menjadi simfoni performa yang presisi.

Transisi dari kepanikan dini hari ini menuntut kita untuk memahami secara mendalam apa yang sebenarnya terjadi di balik dinding penyimpanan data MariaDB.

## 2. Di Balik Layar: Bagaimana Sistem Ini Bekerja Tanpa Sihir

Banyak engineer pemula menganggap database relasional seperti MariaDB sebagai kotak hitam ajaib: Anda lempar query, data keluar. Padahal, di dalamnya ada mekanika mesin yang kompleks, terutama ketika kita menggunakan storage engine bawaan modern seperti InnoDB.

Ketika sebuah query masuk melalui koneksi TCP, SQL Parser menterjemahkannya menjadi struktur pohon (parse tree). Di sinilah Optimizer mengambil alih peran seorang dirigen orkestra. Berdasarkan statistik tabel yang tersimpan di dalam sistem, optimizer memutuskan jalan mana yang paling efisien: apakah harus melakukan *Full Table Scan* yang melelahkan, atau menggunakan *Index Range Scan* yang anggun.

Namun, optimizer tidak selalu benar. Jika statistik data sudah usang atau struktur indeks tidak selaras dengan klausa `WHERE` dan `JOIN`, optimizer akan salah mengambil keputusan. Ia akan memilih membaca jutaan baris dari disk (atau *Buffer Pool* yang sempit) satu per satu. Bayangkan Anda mencari sebuah nama di buku telepon setebal 10.000 halaman dengan membaca setiap barisnya dari awal hingga akhir, ketimbang langsung membuka indeks abjad.

Di sinilah pentingnya memahami anatomi pembacaan data, yang akan membawa kita pada persimpangan krusial dalam memilih strategi optimasi.

## 3. Pilihan Pendekatan & Kompromi yang Harus Diambil

Dalam dunia arsitektur sistem skala besar, tidak ada peluru perak. Setiap keputusan optimasi selalu datang dengan biaya kompromi (*trade-off*).

Secara garis besar, ketika menghadapi performa MariaDB yang jeblok, kita dihadapkan pada tiga jalur utama:

1. **Query & Index Tuning**: Mengubah struktur query dan menambahkan indeks komposit. 
   - *Kelebihan*: Tidak memerlukan perubahan arsitektur aplikasi yang masif, hemat biaya infrastruktur.
   - *Kekurangan*: Membutuhkan ketelitian tinggi; salah membuat indeks justru memperlambat proses `INSERT` dan `UPDATE`.

2. **Database Caching & Redis Layer**: Menaruh lapisan Redis di antara backend Node.js/Go dan MariaDB untuk data yang sering dibaca.
   - *Kelebihan*: Mengurangi beban database secara drastis untuk data panas (*hot data*).
   - *Kekurangan*: Kompleksitas sinkronisasi data (*cache invalidation*) meningkat.

3. **Sharding & Partisioning Tabel**: Memecah tabel besar berdasarkan rentang waktu atau wilayah.
   - *Kelebihan*: Skalabilitas horizontal tanpa batas.
   - *Kekurangan*: Kompleksitas kueri lintas partisi dan perawatan operasional yang jauh lebih berat.

Untuk kasus trafo skala juta pengguna dengan beban tulis (*write-heavy*) yang masif, kombinasi antara **Index Tuning tingkat lanjut** dan **Konfigurasi Buffer Pool yang agresif** adalah harga mati sebelum kita melangkah ke arsitektur caching yang lebih rumit. Mari kita siapkan lingkungan kerjanya.

## 4. Meracik Lingkungan: Persiapan & Prasyarat Lapangan

Sebelum kita menyentuh konfigurasi inti MariaDB, pastikan fondasi sistem operasi Anda di bawah kendali penuh. Seringkali performa database terhambat bukan karena MariaDB-nya, melainkan karena sistem operasi Linux yang masih menggunakan setelan standar pabrik.

Masuklah ke server database Anda menggunakan SSH, lalu mari kita periksa batas maksimal file terbuka (*open files*) dan konfigurasi kernel:

```bash
# Periksa batas maksimal open files sistem operasi
ulimit -n

# Edit konfigurasi limits.d untuk memastikan MariaDB tidak kehabisan descriptor file
sudo nano /etc/security/limits.d/99-mariadb.conf
```

Tambahkan baris konfigurasi berikut ke dalam file tersebut:

```text
mariadb soft nofile 65535
mariadb hard nofile 65535
mariadb soft nproc 32768
mariadb hard nproc 32768
```

> [!TIP]
> Pastikan Anda menjalankan MariaDB di atas filesystem seperti XFS atau Ext4 dengan opsi mount `noatime,nodiratime` untuk menghindari penulisan metadata waktu akses yang tidak perlu setiap kali data dibaca.

Dengan lingkungan sistem operasi yang telah dijinakkan, kini saatnya kita masuk ke inti dari perbaikan performa: membaca dan memahami kebiasaan buruk database melalui log.

## 5. Langkah Implementasi: Membangun Solusi yang Tangguh

Langkah pertama dalam menyembuhkan sistem yang sakit adalah diagnosis yang akurat. Kita harus mengaktifkan dan membaca **Slow Query Log** tanpa membuat server semakin tersiksa.

Buka konsol MariaDB Anda, lalu jalankan perintah berikut untuk mengaktifkan pencatatan query lambat secara dinamis tanpa perlu merestart layanan:

```sql
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5; -- Catat query yang memakan waktu lebih dari 500 milidetik
SET GLOBAL log_queries_not_using_indexes = 'ON';
```

Namun, membaca file log mentah berukuran gigabytes dengan mata telanjang adalah siksaan. Di sinilah kita menggunakan alat bantu seperti `mysqldumpslow` atau `Percona Toolkit` (`pt-query-digest`) untuk merangkum query mana yang paling sering mengeksekusi *Full Table Scan*.

```bash
# Menganalisis log query lambat untuk mencari biang keladi utama
sudo pt-query-digest /var/lib/mysql/mariadb-slow.log > /tmp/slow_report.txt
cat /tmp/slow_report.txt | head -n 50
```

Dari laporan tersebut, kita biasanya menemukan pola query telemetri trafo yang mengabaikan indeks waktu. Mari kita bedah baris kodenya pada bagian berikutnya.

## 6. Membedah Baris Kode Krusial yang Kerap Menjebak

Perhatikan contoh query laporan telemetri di bawah ini yang sering ditulis oleh backend engineer tanpa menyadari dampak destruktifnya pada tabel berukuran 100 juta baris:

```sql
-- ❌ CONTOH BURUK: Memicu Full Table Scan dan fungsi pada kolom terindeks
SELECT 
    transformer_id, 
    AVG(voltage) as avg_voltage, 
    MAX(current) as max_current
FROM transformer_telemetry
WHERE DATE(recorded_at) = '2023-10-25'
GROUP BY transformer_id;
```

> [!IMPORTANT]
> Penggunaan fungsi wrapper seperti `DATE(recorded_at)` pada klausa `WHERE` membuat MariaDB **tidak dapat menggunakan indeks** yang melekat pada kolom `recorded_at`. Database terpaksa mendekode tanggal dari setiap baris satu per satu!

Mari kita ubah query tersebut menjadi bentuk yang ramah indeks (*sargable*):

```sql
-- ✅ CONTOH BENAR: Memanfaatkan Range Scan secara efisien
SELECT 
    transformer_id, 
    AVG(voltage) as avg_voltage, 
    MAX(current) as max_current
FROM transformer_telemetry
WHERE recorded_at >= '2023-10-25 00:00:00'
  AND recorded_at < '2023-10-26 00:00:00'
GROUP BY transformer_id;
```

Selain itu, kita perlu membangun **Strategi Indexing Tingkat Lanjut** berupa *Composite Index* yang mencakup kolom pencarian sekaligus kolom pengelompokan:

```sql
-- Membuat composite index untuk mempercepat filter waktu dan grouping ID
CREATE INDEX idx_telemetry_time_transformer 
ON transformer_telemetry (recorded_at, transformer_id, voltage, current);
```

Dengan struktur indeks komposit ini, MariaDB bahkan dapat menyelesaikan query langsung dari struktur indeks tanpa harus membuka data aktual di tabel (*Covering Index*).

## 7. Bertahan di Bawah Tekanan: Skenario Traffic Nyata

Optimasi query tidak akan ada artinya jika konfigurasi memori server masih menggunakan nilai default. Untuk server dengan RAM 64GB yang didedikasikan untuk database, kita harus menyetel **Buffer Pool** agar mampu menampung sebagian besar data panas di dalam memori RAM.

Buka file konfigurasi utama MariaDB (`/etc/mysql/mariadb.conf.d/50-server.cnf` atau `/etc/my.cnf`):

```ini
[mysqld]
# Alokasikan 70-80% dari total RAM server untuk InnoDB Buffer Pool
innodb_buffer_pool_size = 48G

# Pecah buffer pool menjadi beberapa instance untuk mengurangi kontensi mutex
innodb_buffer_pool_instances = 8

# Optimalkan ukuran log file untuk performa tulis yang lebih tinggi
innodb_log_file_size = 2G
innodb_flush_log_at_trx_commit = 2

# Konfigurasi Thread Concurrency
thread_cache_size = 100
max_connections = 2000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
```

> [!TIP]
> Mengatur `innodb_flush_log_at_trx_commit = 2` memberikan peningkatan performa tulis yang luar biasa untuk sistem IoT telemetri, dengan risiko kehilangan data maksimal hanya 1 detik jika terjadi kegagalan daya total pada server fisik.

Konfigurasi ini memastikan bahwa saat jutaan pengguna dan perangkat mengirimkan data secara serentak, antrean di tingkat sistem operasi dan mesin penyimpanan tetap berjalan mulus tanpa tersendat.

## 8. Jebakan Klasik & Cara Menghindarinya Sebelum Meledak

Selama bertahun-tahun menangani infrastruktur skala besar, saya melihat pola kesalahan berulang yang dilakukan oleh tim engineering. Berikut adalah perangkap klasik yang wajib Anda hindari:

1. **Melakukan `SELECT *` Tanpa Batas (`LIMIT`)**: Mengambil seluruh kolom pada tabel lebar menghabiskan bandwidth jaringan internal dan memori PHP-FPM secara instan.
2. **Penggunaan `ORDER BY RAND()`**: Perintah ini memaksa MariaDB membuat tabel temporary di disk untuk mengacak jutaan baris. Gunakan pendekatan logika aplikasi jika ingin mengambil data acak.
3. **Mengabaikan Deadlock pada Transaksi Bersamaan**: Ketika banyak worker backend memperbarui status trafo secara bersamaan, deadlock pasti terjadi. Pastikan urutan pengaksesan baris tabel selalu konsisten di dalam kode aplikasi Anda.

Menghindari jebakan ini menyelamatkan sistem dari kehancuran mendadak di tengah malam.

## 9. Uji Nyali: Mengukur Performa di Titik Batas

Jangan pernah mendeklarasikan sebuah sistem "siap produksi" sebelum Anda menyiksanya menggunakan alat uji beban (*load testing*). Kita menggunakan **Sysbench** untuk mensimulasikan jutaan transaksi konkuren.

Jalankan perintah berikut di lingkungan staging untuk melakukan benchmark pada engine InnoDB:

```bash
# Persiapkan data uji sebanyak 10 juta baris
sysbench /usr/share/sysbench/oltp_read_write.lua \
    --mysql-host=127.0.0.1 \
    --mysql-user=root \
    --mysql-password=secret_password \
    --mysql-db=testdb \
    --table-size=10000000 \
    prepare

# Jalankan simulasi beban baca-tulis selama 5 menit dengan 64 thread
sysbench /usr/share/sysbench/oltp_read_write.lua \
    --mysql-host=127.0.0.1 \
    --mysql-user=root \
    --mysql-password=secret_password \
    --mysql-db=testdb \
    --table-size=10000000 \
    --threads=64 \
    --time=300 \
    --report-interval=10 \
    run
```

Amati grafik latensi dan *Transactions Per Second* (TPS) melalui Grafana dan Prometheus. Jika latensi rata-rata berada di bawah 15 milidetik di bawah tekanan 64 thread konkuren, arsitektur database Anda telah siap menghadapi dunia nyata.

## 10. Catatan Lapangan & Checklist Sebelum Rilis

Sebelum Anda menekan tombol *deploy* ke lingkungan produksi, luangkan waktu sejenak untuk meninjau kembali daftar periksa (*checklist*) operasional berikut:

- [ ] Pastikan semua query baru telah diuji menggunakan perintah `EXPLAIN` untuk memvalidasi penggunaan indeks.
- [ ] Konfigurasi ulang `innodb_buffer_pool_size` sesuai dengan kapasitas RAM server produksi baru.
- [ ] Aktifkan rotasi log untuk `slow_query_log` agar tidak menghabiskan kapasitas disk (*disk exhaustion*).
- [ ] Verifikasi bahwa sistem pemantauan (Prometheus Exporter / Datadog) mencatat metrik *Threads_running* dan *Slow_queries* secara real-time.
- [ ] Buat dokumentasi pemeliharaan rutin dan skenario *failover* replikasi master-slave.

Dengan pondasi arsitektur query yang kokoh, indeks yang presisi, dan manajemen memori yang matang, sistem trafo skala juta pengguna Anda tidak hanya akan bertahan dari lonjakan beban, tetapi juga melaju kencang tanpa kompromi.