โ† Semua pembelajaran / Astro Nol โ†’ Portal Berita
Fase 10 ยท Deploy di AWS

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, dan slow_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-AZRead replica
UntukKetersediaanKapasitas baca
Bisa dibaca aplikasiTidak (standby pasif)Ya
ReplikasiSinkronAsinkron
FailoverOtomatis, 1โ€“2 menitManual (promosi)
Biaya2ร— instance1ร— 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"
ParameterKenapa
max_connectionsHarus cocok dengan hitunganmu dari Fase 2 dan batas autoscaling
slow_query_log + long_query_time=0.5Menemukan kueri yang menghabiskan database
wait_timeout=300Koneksi menganggur dibersihkan; pool aplikasimu menyambung ulang
innodb_flush_log_at_trx_commit=1Jangan turunkan. Nilai 2 lebih cepat tapi bisa kehilangan transaksi saat crash โ€” untuk data pembayaran itu tidak bisa diterima
character_set_server=utf8mb4Tabel 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
JenisUntukRetensi
Cadangan otomatis + PITRPemulihan ke titik waktu mana pun14 hari
Snapshot manual sebelum migrasi besarRollback yang tidak bisa dilakukan lewat kodeSampai dihapus manual
Ekspor ke S3Arsip jangka panjang, analitikSesuai 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 dilihatArtinya
Kueri teratas berdasarkan waktu totalBeban sesungguhnya โ€” bukan yang paling lambat
Beban melebihi garis vCPUDatabase jadi batasnya
Wait event io/table/sql/handlerMembaca dari disk โ€” buffer pool kurang atau index hilang
Wait event synch/mutex/*Kontensi โ€” sering karena penghitung hits (Fase 2)

Alarm yang layak dipasang

MetrikAmbangKenapa
DatabaseConnections> 70% max_connectionsMenuju kegagalan yang dibahas di Fase 2
CPUUtilization> 80% selama 10 menitโ€”
FreeableMemory< 15%Buffer pool tertekan
ReplicaLag> 5 detikPembaca melihat data basi (Fase 2)
FreeStorageSpace< 20%Database penuh = tulisan gagal total
ReadIOPS / WriteIOPSMendekati batas provisionedTerbatas 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.