Hitungan Back-of-Envelope

Estimasi QPS, penyimpanan, dan bandwidth dengan disiplin pangkat sepuluh dan angka yang mudah diperiksa ulang.

Daftar isi

Hitungan back-of-envelope (back-of-the-envelope calculation) adalah perhitungan kasar yang digunakan untuk menguji kelayakan sebuah desain dengan aritmetika sederhana. Topik ini membahas disiplin penulisannya, lalu memberikan contoh estimasi QPS, penyimpanan, dan bandwidth yang seluruh aritmetikanya benar-benar dieksekusi sehingga keluarannya dapat diperiksa ulang. Setelah membacanya, sasaran yang sebelumnya hanya berupa target kini dapat diturunkan menjadi angka kapasitas yang berdiri di atas asumsi yang terbuka.

Esensi hitungan back-of-envelope#

Hitungan back-of-envelope menggunakan asumsi paling sederhana untuk menjawab pertanyaan orde besar: berapa banyak permintaan, berapa besar data, dan berapa lebar saluran yang diperlukan. Tujuannya bukan ketepatan, melainkan memastikan desain berada pada pangkat sepuluh yang benar. Rancangan yang mengasumsikan sepuluh ribu permintaan per detik padahal kebutuhan sebenarnya sepuluh juta akan gagal, berapa pun rinciannya, dan jenis kegagalan inilah yang ingin ditangkap lebih dahulu.

Disiplinnya ada tiga. Pertama, bulatkan semua masukan ke angka yang mudah dikalikan, karena 10610^6 lebih mudah diperiksa ulang daripada 1.274.000. Kedua, tulis setiap asumsi, sehingga pembaca lain dapat menggantinya dan menghitung ulang. Ketiga, nyatakan hasil pada pangkat sepuluh yang bersangkutan, bukan pada angka yang terlihat presisi. Tabel berikut memuat pangkat sepuluh yang paling sering digunakan beserta unit penyimpanannya.

PangkatNilaiNamaUnit penyimpanan
10310^31.000ribu1 KB
10610^61 jutajuta1 MB
10910^91 miliarmiliar1 GB
101210^{12}1 triliuntriliun1 TB

Konstanta waktu juga perlu dihafal. Berkas berikut mencetak beberapa konstanta yang paling sering digunakan, termasuk fakta bahwa satu juta detik hanya sekitar dua belas hari, perbandingan yang sering keliru saat estimasi dilakukan dalam satuan detik.

python konstanta-estimasi.py
# Konstanta pangkat sepuluh yang lazim digunakan untuk hitungan kasar.

print("Detik per hari:", 24 * 60 * 60)
print("Detik per bulan, 30 hari:", 30 * 24 * 60 * 60)
print("Detik per tahun, 365 hari:", 365 * 24 * 60 * 60)
print("Satu juta detik dalam hari:", round(10**6 / 86_400, 1))
print("Sepuluh juta detik dalam hari:", round(10**7 / 86_400))
text Keluaran
Detik per hari: 86400
Detik per bulan, 30 hari: 2592000
Detik per tahun, 365 hari: 31536000
Satu juta detik dalam hari: 11.6
Sepuluh juta detik dalam hari: 116

Estimasi QPS#

QPS (queries per second) menghitung berapa banyak permintaan yang harus dilayani setiap detik, dan bersama throughput, yaitu volume kerja yang selesai per satuan waktu, menjadi ukuran beban yang paling mendasar. Estimasinya diturunkan dari pengguna aktif harian dan frekuensi pemakaian:

QPSrata-rata=permintaan per hari86 400 \text{QPS}_{\text{rata-rata}} = \frac{\text{permintaan per hari}}{86\,400}

Berkas berikut mengestimasi beban layanan dengan satu juta pengguna aktif harian yang masing-masing mengirim sepuluh permintaan.

python estimasi-qps.py
# Estimasi QPS dari pengguna aktif harian dan permintaan per pengguna.

PENGGUNA_AKTIF_HARIAN = 1_000_000   # 10^6 pengguna aktif per hari
PERMINTAAN_PER_PENGGUNA = 10        # rata-rata 10 permintaan per pengguna
FAKTOR_PUNCAK = 3                   # trafik puncak lazimnya 2 sampai 4 kali rata-rata

total_permintaan = PENGGUNA_AKTIF_HARIAN * PERMINTAAN_PER_PENGGUNA
qps_rata_rata = total_permintaan / (24 * 60 * 60)
qps_puncak = qps_rata_rata * FAKTOR_PUNCAK

print(f"Total permintaan per hari : {total_permintaan:,}")
print(f"QPS rata-rata             : {qps_rata_rata:,.1f}")
print(f"QPS puncak                : {qps_puncak:,.1f}")
text Keluaran
Total permintaan per hari : 10,000,000
QPS rata-rata             : 115.7
QPS puncak                : 347.2

Hasilnya sekitar 116 permintaan per detik pada kondisi rata-rata. Trafik tidak pernah datar, sehingga kapasitas tidak dibangun untuk rata-rata, melainkan untuk puncak. Faktor puncak yang lazim digunakan berkisar dua sampai empat kali rata-rata, sehingga dengan faktor tiga, kapasitas yang harus tersedia sekitar 350 permintaan per detik. Angka ini menjadi masukan bagi keputusan berikutnya, misalnya jumlah server yang berjalan dan kapasitas load balancer. Perhitungan kebalikannya juga berguna: bila satu server diketahui mampu melayani dua ratus permintaan per detik, angka puncak 350 langsung menunjukkan kebutuhan minimal dua server.

Estimasi penyimpanan#

Penyimpanan dihitung dari jumlah catatan baru dan ukuran tiap catatan, lalu diproyeksikan ke periode yang lebih panjang:

penyimpanan per hari=catatan baru per hari×byte per catatan \text{penyimpanan per hari} = \text{catatan baru per hari} \times \text{byte per catatan}

Berkas berikut mengestimasi pertumbuhan penyimpanan layanan pencatatan sensor suhu yang menulis 50 juta catatan per hari dengan ukuran 500 byte per catatan.

python estimasi-penyimpanan.py
# Estimasi penyimpanan untuk catatan log sensor suhu.

CATATAN_PER_HARI = 50_000_000    # 5 x 10^7 catatan baru per hari
UKURAN_CATATAN_BYTE = 500        # 500 byte per catatan

per_hari = CATATAN_PER_HARI * UKURAN_CATATAN_BYTE
per_tahun = per_hari * 365

print(f"Penyimpanan per hari  : {per_hari / 10**9:.1f} GB")
print(f"Penyimpanan per tahun : {per_tahun / 10**12:.2f} TB")
text Keluaran
Penyimpanan per hari  : 25.0 GB
Penyimpanan per tahun : 9.12 TB

Hasilnya 25 GB per hari dan sekitar 9 TB per tahun. Dua angka desimal pada keluaran adalah produk format, bukan sinyal ketepatan, sehingga pelaporannya cukup dengan sekitar 9 TB. Proyeksi seperti ini langsung menentukan keputusan: tabel basis data yang tumbuh 25 GB per hari perlu rencana partisi sejak awal, dan data yang berumur lebih dari satu tahun dapat dipindahkan ke penyimpanan yang lebih murah untuk menahan biaya.

Estimasi bandwidth#

Bandwidth menghitung volume data yang melintas per detik pada sisi masuk (ingress) dan sisi keluar (egress). Hitungannya mengalikan QPS dengan ukuran payload, lalu mengalikannya dengan delapan karena satuan jaringan berupa bit, sedangkan ukuran berkas berupa byte:

bit per detik=QPS×byte per permintaan×8 \text{bit per detik} = \text{QPS} \times \text{byte per permintaan} \times 8

Berkas berikut melanjutkan estimasi QPS sebelumnya dengan asumsi permintaan 1 KB dan respons 10 KB.

python estimasi-bandwidth.py
# Estimasi bandwidth dari QPS puncak dan ukuran payload.
# Byte dikalikan 8 agar satuannya berubah menjadi bit.

QPS_PUNCAK = 350                  # hasil estimasi QPS puncak sebelumnya
UKURAN_PERMINTAAN_BYTE = 1_000    # 1 KB per permintaan masuk
UKURAN_RESPONS_BYTE = 10_000      # 10 KB per respons keluar

ingress_bps = QPS_PUNCAK * UKURAN_PERMINTAAN_BYTE * 8
egress_bps = QPS_PUNCAK * UKURAN_RESPONS_BYTE * 8

print(f"Bandwidth ingress : {ingress_bps / 10**6:.1f} Mbps")
print(f"Bandwidth egress  : {egress_bps / 10**6:.1f} Mbps")
text Keluaran
Bandwidth ingress : 2.8 Mbps
Bandwidth egress  : 28.0 Mbps

Pada trafik puncak, saluran masuk hanya 2,8 Mbps, sedangkan saluran keluar 28 Mbps, sepuluh kali lebih besar karena responsnya lebih panjang daripada permintaannya. Ketimpangan seperti ini lazim pada layanan yang mengirim data lebih banyak daripada yang diterimanya, dan menunjukkan sisi yang perlu diukur lebih dahulu ketika kapasitas jaringan ditentukan. Apabila respons dapat dipadatkan atau dipangkas ukurannya, angka egress turun sebanding, sehingga estimasi sederhana ini juga membantu menilai upaya optimasi mana yang berdampak paling besar. Kedua angka tersebut juga menjadi patokan saat memilih kelas jaringan pada penyedia infrastruktur, karena kapasitas yang dipesan kurang akan tampak sebagai latensi yang naik tanpa penyebab yang jelas.

Kalkulator untuk memeriksa ulang#

Estimasi yang diulang berkali-kali lebih cepat dengan alat bantu. Demo berikut menghitung ulang seluruh angka pada topik ini dari empat masukan yang sama, yaitu pengguna aktif harian, permintaan per pengguna, ukuran respons, dan faktor puncak. Ubah salah satu nilai, kemudian amati bagaimana QPS, penyimpanan, dan bandwidth bergeser bersamaan. Mengganti pengguna aktif menjadi sepuluh juta, misalnya, harus memindahkan seluruh hasil satu pangkat sepuluh. Satu asumsi perlu diingat: penyimpanan pada demo mengasumsikan seluruh payload respons tersimpan, sedangkan pada praktiknya hanya sebagian data yang disimpan.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Kalkulator estimasi back-of-envelope</title>
<style>
  body { font-family: system-ui, sans-serif; margin: 0; padding: 14px; font-size: 14px; color: #22313f; }
  form { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 14px; margin-bottom: 14px; }
  label { display: flex; flex-direction: column; gap: 3px; font-size: 12px; font-weight: 600; }
  input { padding: 6px 8px; border: 1px solid #b6c2cf; border-radius: 4px; font-size: 14px; font-weight: 400; }
  table { border-collapse: collapse; width: 100%; }
  td { padding: 6px 8px; border-bottom: 1px solid #e4e9ee; font-size: 13px; }
  td:last-child { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; }
</style>
</head>
<body>
<form id="masukan">
  <label>Pengguna aktif per hari
    <input id="pengguna" type="number" value="1000000" min="1">
  </label>
  <label>Permintaan per pengguna per hari
    <input id="permintaan" type="number" value="10" min="1">
  </label>
  <label>Ukuran respons (byte)
    <input id="payload" type="number" value="10000" min="1">
  </label>
  <label>Faktor puncak
    <input id="puncak" type="number" value="3" min="1" step="0.5">
  </label>
</form>
<table id="hasil"></table>
<script>
  var kolom = ["B", "KB", "MB", "GB", "TB"];

  function satuan(byte) {
    var i = 0;
    while (byte >= 1000 && i < kolom.length - 1) { byte /= 1000; i++; }
    if (byte < 10) return byte.toFixed(2) + " " + kolom[i];
    if (byte < 100) return byte.toFixed(1) + " " + kolom[i];
    return Math.round(byte) + " " + kolom[i];
  }

  function hitung() {
    var pengguna = Number(document.getElementById("pengguna").value) || 0;
    var permintaan = Number(document.getElementById("permintaan").value) || 0;
    var payload = Number(document.getElementById("payload").value) || 0;
    var puncak = Number(document.getElementById("puncak").value) || 0;
    var perHari = pengguna * permintaan;
    var qpsRata = perHari / 86400;
    var qpsPuncak = qpsRata * puncak;
    var simpanHari = perHari * payload;
    var egressMbps = qpsPuncak * payload * 8 / 1000000;
    var baris = [
      ["Total permintaan per hari", Math.round(perHari).toLocaleString("id-ID")],
      ["QPS rata-rata", qpsRata.toFixed(1)],
      ["QPS puncak", qpsPuncak.toFixed(1)],
      ["Penyimpanan per hari", satuan(simpanHari)],
      ["Bandwidth egress puncak", egressMbps.toFixed(1) + " Mbps"]
    ];
    var html = "";
    for (var i = 0; i < baris.length; i++) {
      html += "<tr><td>" + baris[i][0] + "</td><td>" + baris[i][1] + "</td></tr>";
    }
    document.getElementById("hasil").innerHTML = html;
  }

  document.getElementById("masukan").addEventListener("input", hitung);
  hitung();
</script>
</body>
</html>

Kalkulator hanya memindahkan aritmetika yang sudah ditunjukkan oleh berkas Python pada bagian sebelumnya, sehingga kedua cara menghasilkan angka yang sama untuk masukan yang sama. Alat bantu semacam ini mempercepat pemeriksaan ulang, tetapi tidak menggantikan asumsi, karena angka yang dihitung ulang dengan asumsi yang keliru tetap keliru. Dengan demikian, kalkulator hanya menghemat waktu, sedangkan tanggung jawab atas asumsi tetap ada pada penulis desain. Karena itu setiap hasil estimasi tetap disertai daftar asumsinya, apa pun alat yang digunakan.

Kesalahan umum#

Kesalahan berikut paling sering membuat hitungan kasar meleset.

  1. Mencampur satuan desimal dan biner. Mengalikan dengan 1.024 pada satu baris dan 1.000 pada baris lain menggeser hasil tanpa disadari. Selisih yang tampak kecil pada satu gigabyte menjadi ratusan gigabyte pada skala terabyte. Perbaikannya: pilih satu konvensi pada seluruh hitungan, dan untuk estimasi, konvensi desimal 1.000 lebih mudah dikalikan serta diperiksa ulang.
  2. Menggunakan pengguna terdaftar alih-alih pengguna aktif. Jumlah akun yang pernah mendaftar jauh lebih besar daripada yang benar-benar menggunakan layanan setiap hari, sehingga kapasitas yang diestimasi meleset dari kebutuhan sebenarnya. Perbaikannya: tetapkan definisi pengguna aktif harian lebih dahulu dan gunakan angka itu secara konsisten pada setiap hitungan.
  3. Melupakan faktor puncak. Kapasitas yang dihitung dari QPS rata-rata habis tepat ketika pengguna paling banyak menggunakan layanan. Perbaikannya: kalikan rata-rata dengan faktor puncak yang dinyatakan secara eksplisit, biasanya dua sampai empat, lalu jadikan angka puncak sebagai dasar kapasitas. Angka puncak juga sebaiknya dihitung ulang setiap kali pola pemakaian pengguna berubah.

Ringkasan#

  • Hitungan back-of-envelope memvalidasi pangkat sepuluh, bukan ketepatan, sehingga masukan dibulatkan, asumsi ditulis, dan hasil dilaporkan pada orde yang benar.
  • QPS diturunkan dari permintaan per hari dibagi 86.400 detik, kemudian dikalikan faktor puncak dua sampai empat untuk menentukan kapasitas.
  • Penyimpanan adalah catatan baru dikali ukuran catatan, diproyeksikan per hari dan per tahun dengan satuan desimal 1.000.
  • Bandwidth mengalikan QPS puncak dengan ukuran payload dan delapan bit per byte, dan pada layanan yang mengirim data lebih banyak, egress biasanya jauh melebihi ingress.

Referensi#