Mengoperasikan RDS MySQL
Database adalah satu-satunya bagian arsitekturmu yang kehilangannya tidak bisa diperbaiki dengan deploy ulang. Ini yang harus benar.
Intisari
- Multi-AZ untuk failover otomatis; read replica untuk kapasitas. Keduanya berbeda dan bisa dipakai bersamaan.
- Parameter group kustom:
max_connections, timeout, danslow_query_log. - Cadangan yang tidak pernah diuji pemulihannya bukan cadangan.
- Failover memutus seluruh koneksi โ aplikasimu harus menyambung ulang, bukan mati.
- Performance Insights adalah cara tercepat menemukan kueri yang menghabiskan database-mu.
Multi-AZ vs read replica
| Multi-AZ | Read replica | |
|---|---|---|
| Untuk | Ketersediaan | Kapasitas baca |
| Bisa dibaca aplikasi | Tidak (standby pasif) | Ya |
| Replikasi | Sinkron | Asinkron |
| Failover | Otomatis, 1โ2 menit | Manual (promosi) |
| Biaya | 2ร instance | 1ร per replica |
Untuk portal produksi: keduanya. Multi-AZ supaya kegagalan AZ tidak mematikan portal, plus satu read replica untuk menyerap beban baca (Fase 2).
Parameter group
aws rds create-db-parameter-group \
--db-parameter-group-name portal-mysql80 \
--db-parameter-group-family mysql8.0 \
--description "Portal berita"
aws rds modify-db-parameter-group \
--db-parameter-group-name portal-mysql80 \
--parameters \
"ParameterName=max_connections,ParameterValue=800,ApplyMethod=pending-reboot" \
"ParameterName=slow_query_log,ParameterValue=1,ApplyMethod=immediate" \
"ParameterName=long_query_time,ParameterValue=0.5,ApplyMethod=immediate" \
"ParameterName=log_output,ParameterValue=FILE,ApplyMethod=immediate" \
"ParameterName=wait_timeout,ParameterValue=300,ApplyMethod=immediate" \
"ParameterName=interactive_timeout,ParameterValue=300,ApplyMethod=immediate" \
"ParameterName=innodb_flush_log_at_trx_commit,ParameterValue=1,ApplyMethod=immediate" \
"ParameterName=character_set_server,ParameterValue=utf8mb4,ApplyMethod=pending-reboot" \
"ParameterName=collation_server,ParameterValue=utf8mb4_0900_ai_ci,ApplyMethod=pending-reboot"
| Parameter | Kenapa |
|---|---|
max_connections | Harus cocok dengan hitunganmu dari Fase 2 dan batas autoscaling |
slow_query_log + long_query_time=0.5 | Menemukan kueri yang menghabiskan database |
wait_timeout=300 | Koneksi menganggur dibersihkan; pool aplikasimu menyambung ulang |
innodb_flush_log_at_trx_commit=1 | Jangan turunkan. Nilai 2 lebih cepat tapi bisa kehilangan transaksi saat crash โ untuk data pembayaran itu tidak bisa diterima |
character_set_server=utf8mb4 | Tabel baru otomatis benar (Fase 9) |
wait_timeout harus lebih pendek dari umur koneksi di pool-mu, atau sebaliknya kamu
akan melihat error "server has gone away". Yang terjadi: MySQL menutup koneksi menganggur, pool
mysql2 tidak tahu, lalu memakai koneksi mati itu untuk kueri berikutnya.
mysql2 menangani sebagian besar kasus ini, tapi menyetel wait_timeout yang
masuk akal mengurangi kejadiannya.
Cadangan
aws rds modify-db-instance \
--db-instance-identifier portal \
--backup-retention-period 14 \
--preferred-backup-window "18:00-19:00" \
--copy-tags-to-snapshot \
--apply-immediately
| Jenis | Untuk | Retensi |
|---|---|---|
| Cadangan otomatis + PITR | Pemulihan ke titik waktu mana pun | 14 hari |
| Snapshot manual sebelum migrasi besar | Rollback yang tidak bisa dilakukan lewat kode | Sampai dihapus manual |
| Ekspor ke S3 | Arsip jangka panjang, analitik | Sesuai kebijakan |
Point-in-time recovery adalah yang menyelamatkanmu dari kesalahan manusia. Skenario nyata:
seseorang menjalankan UPDATE artikel SET status='draf' tanpa WHERE pada pukul
14.23. Snapshot semalam kehilangan seluruh pekerjaan hari itu; PITR mengembalikan keadaan pukul 14.22.
Pastikan retensinya cukup panjang untuk menemukan kesalahan sebelum jendelanya lewat.
Uji pemulihan โ ini bagian yang dilewati orang
# Pulihkan ke instance BARU. Jangan sentuh yang produksi.
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier portal \
--target-db-instance-identifier portal-uji-pulih \
--restore-time "2026-08-27T07:22:00Z" \
--db-instance-class db.t4g.medium \
--no-multi-az
# Setelah tersedia, verifikasi datanya
mysql -h portal-uji-pulih.abc.rds.amazonaws.com -u admin -p -e "
SELECT COUNT(*) AS artikel FROM portal.artikel;
SELECT COUNT(*) AS member FROM portal.member;
SELECT MAX(terbit_pada) FROM portal.artikel;
SELECT COUNT(*) AS langganan_aktif FROM portal.langganan WHERE status='aktif';"
# Jangan lupa hapus โ instance uji ditagih per jam
aws rds delete-db-instance --db-instance-identifier portal-uji-pulih \
--skip-final-snapshot
Lakukan ini sekali per kuartal, dan catat berapa lama. Angka itu adalah RTO-mu yang sesungguhnya โ waktu yang dibutuhkan untuk kembali beroperasi setelah kehilangan database. Kalau ternyata empat jam dan bisnis mengharapkan satu jam, kamu baru saja menemukan kesenjangan yang perlu dibicarakan sebelum ada insiden, bukan sesudahnya.
Failover
Yang terjadi saat failover Multi-AZ:
1. RDS mendeteksi kegagalan
2. Standby dipromosikan
3. Record DNS endpoint diarahkan ke instance baru
4. SELURUH koneksi yang ada TERPUTUS
5. Total: 60โ120 detik
// Pool harus menyambung ulang, bukan menyerah.
const pool = createPool({
host: env.DB_HOST_RW,
connectionLimit: 4,
// Jangan cache DNS lama โ endpoint menunjuk ke instance baru setelah failover.
// Node meng-cache DNS; batasi umurnya.
...(process.env.NODE_ENV === "production" && { lookup: dnsLookupTanpaCache }),
});
pool.on("error", (e) => {
// Ditangkap di sini alih-alih membunuh proses.
console.error(JSON.stringify({ level: "error", pool: String(e) }));
});
# Latih failover di jam sepi. Ini satu-satunya cara tahu aplikasimu selamat.
aws rds reboot-db-instance --db-instance-identifier portal --force-failover
# Sambil itu berjalan, pantau dari sisi aplikasi:
while true; do
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://portal.contoh.id/siap
sleep 1
done
Node meng-cache hasil DNS, dan itu masalah nyata saat failover. Endpoint RDS adalah nama DNS yang berubah tujuannya saat failover. Kalau proses Node-mu meng-cache resolusi lama, ia akan terus mencoba menghubungi instance yang sudah mati โ sampai proses di-restart. Uji ini secara eksplisit; gejalanya adalah "situs mati padahal RDS bilang sudah pulih".
Performance Insights
aws rds modify-db-instance \
--db-instance-identifier portal \
--enable-performance-insights \
--performance-insights-retention-period 7 \
--apply-immediately
| Yang dilihat | Artinya |
|---|---|
| Kueri teratas berdasarkan waktu total | Beban sesungguhnya โ bukan yang paling lambat |
| Beban melebihi garis vCPU | Database jadi batasnya |
Wait event io/table/sql/handler | Membaca dari disk โ buffer pool kurang atau index hilang |
Wait event synch/mutex/* | Kontensi โ sering karena penghitung hits (Fase 2) |
Alarm yang layak dipasang
| Metrik | Ambang | Kenapa |
|---|---|---|
DatabaseConnections | > 70% max_connections | Menuju kegagalan yang dibahas di Fase 2 |
CPUUtilization | > 80% selama 10 menit | โ |
FreeableMemory | < 15% | Buffer pool tertekan |
ReplicaLag | > 5 detik | Pembaca melihat data basi (Fase 2) |
FreeStorageSpace | < 20% | Database penuh = tulisan gagal total |
ReadIOPS / WriteIOPS | Mendekati batas provisioned | Terbatas IO |
Alarm penyimpanan adalah yang paling penting dan paling sering diabaikan. Database yang kehabisan ruang berhenti menerima tulisan sepenuhnya โ pembayaran gagal, artikel tidak bisa terbit, sesi tidak bisa dibuat. Dan pemulihannya tidak instan: menambah penyimpanan butuh waktu. Aktifkan storage autoscaling RDS dan pasang alarmnya.
Latihan: buat parameter group dengan nilai di atas dan terapkan. Aktifkan Performance Insights, lalu buka dan catat lima kueri teratas berdasarkan waktu total โ bandingkan dengan yang kamu duga di Fase 2. Terakhir, dan ini yang paling penting: pulihkan cadangan ke instance uji, verifikasi jumlah barisnya, catat berapa lama, lalu hapus instance-nya.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.