Lompat ke konten
18 menit bacamateri 68 dari 96

Menghitung dalam kondisi berebut sumber daya

Counter atomik, counter terpartisi, batching, dan rollup: kapan satu baris counter jadi bottleneck, dan apa yang dikorbankan tiap solusi.

Ditinjau 26 Oktober 2026

Pertanyaan

Apa yang terjadi saat ribuan request memperbarui satu angka yang sama, dan bagaimana menyebar bebannya tanpa merusak hasilnya?

Setelah baca ini, kamu bisa memilih antara counter atomik, counter terpartisi, batching, dan rollup untuk satu beban, lengkap dengan harga yang dibayar masing-masing.

Prasyarat dan contoh

Kamu sudah punya definisi event dari M12.01 dan paham hot key dari partisi (M07). Kamu juga tahu bahwa UPDATE ... SET n = n + 1 itu aman secara atomik.

Contoh: sebuah postingan menjadi viral dan tombol "suka" menerima 8.000 klik per detik pada satu post_id. Kodenya benar: UPDATE posts SET likes = likes + 1 WHERE id = ?. Tapi database mengunci baris itu selama update, jadi 8.000 transaksi mengantre satu per satu pada baris yang sama. Kalau tiap update menahan kunci 1 ms, batas maksimumnya sekitar 1.000 per detik. Sisanya menumpuk jadi latensi dan akhirnya timeout.

Masalahnya bukan hitungannya salah. Masalahnya semua penulis berebut satu titik serialisasi.

Mekanisme

Ada empat cara, dan masing-masing menukar sesuatu.

1. Counter atomik pada satu tempat. Satu baris atau satu key (misalnya INCR di Redis, yang sanggup puluhan ribu operasi per detik per instance). Hasilnya selalu akurat dan dibaca langsung. Batasnya: kapasitas satu baris atau satu shard. Cocok kalau tulisan per key di bawah beberapa ratus per detik.

2. Counter terpartisi (sharded). Pecah satu counter jadi N baris: likes:{post_id}:{0..N-1}. Tulis ke shard acak, baca dengan menjumlahkan semuanya. Dengan N = 16, batas tulis naik kira-kira 16 kali, tapi pembacaan jadi 16 baca. Harganya: pembacaan lebih mahal, dan hasil jumlah bisa sedikit tertinggal kalau shard dibaca pada waktu berbeda. Kamu juga tidak bisa lagi menegakkan aturan seperti "berhenti di tepat 1.000" tanpa koordinasi.

3. Batching di memori. Setiap server aplikasi menjumlahkan lokal, lalu mengirim satu update per interval, misalnya +312 setiap 1 detik. Delapan ribu klik per detik di 10 server jadi hanya 10 tulisan per detik. Harganya: angka tertunda sampai satu interval, dan batch yang belum dikirim hilang kalau server crash. Kalau kehilangan itu tidak bisa diterima, tulis event ke log tahan lama lebih dulu dan hitung dari log.

4. Rollup. Simpan counter halus (per menit), lalu jumlahkan secara berkala ke counter kasar (per jam, per hari). Tulisan panas hanya menyentuh baris menit. Pembacaan dashboard bulanan membaca 30 baris harian, bukan 43.200 baris menit. Harganya: ada pekerjaan latar belakang yang harus idempoten, dan angka kasar tertinggal dari angka halus.

Saat rancangan ini gagal

  • Shard terlalu sedikit. Dengan N = 4 untuk beban 8.000 per detik, tiap shard masih menerima 2.000 per detik dan tetap jadi hot row. Hitung dulu: beban per shard = beban total / N.
  • Shard acak membuat pembacaan tidak konsisten. Dua pembaca berturut-turut bisa melihat angka yang turun, kalau salah satunya membaca replika yang tertinggal. Untuk "jumlah suka" itu bisa diterima. Untuk "stok tersisa" tidak.
  • Retry menggandakan hitungan. Counter atomik tidak idempoten. Kalau klien me-retry INCR setelah timeout, kamu menghitung dua kali. Hitung dari event ber-ID (M12.01), bukan dari panggilan increment mentah, kalau akurasi penting.
  • Batch hilang saat deploy. Server direstart dan 1 detik data, sekitar 800 klik, hilang. Kalau ini terjadi tiap deploy, angka berkurang konsisten. Flush batch pada sinyal shutdown, dan catat kehilangan itu sebagai batas yang diketahui.

Latihan

Papan peringkat menghitung "reaksi" per postingan. Satu postingan puncaknya 12.000 reaksi per detik. Satu baris database tahan sekitar 800 update per detik. Ada 20 server aplikasi.

  1. Berapa shard minimum kalau memakai counter terpartisi?
  2. Berapa tulisan per detik ke database kalau memakai batching 1 detik di 20 server?
  3. Pilih satu pendekatan untuk papan peringkat yang dilihat pengguna dan sebutkan harganya.
Lihat pembahasan
  1. 12.000 / 800 = 15 shard secara teoretis. Itu tanpa margin, jadi ambil 32 supaya tiap shard berada sekitar 375 per detik, dan distribusi acak yang tidak rata tidak langsung membuatnya panas. Pembacaan jadi menjumlahkan 32 baris.
  2. 20 tulisan per detik (satu per server), masing-masing membawa sekitar 600 reaksi. Itu jauh di bawah batas 800, bahkan kalau dikali beberapa postingan panas bersamaan.
  3. Batching 1 detik, karena papan peringkat tidak butuh angka real-time yang tepat. Harganya: angka tertinggal sampai sekitar 1 detik, dan hingga satu detik reaksi hilang kalau server crash tanpa flush. Kalau kehilangan tidak bisa diterima, tulis ke log durabel dan hitung dari sana.

Perhatikan bahwa jawaban 3 menyebut kerugian, bukan hanya keuntungan. Itu yang membedakan keputusan dari preferensi.

Terkait

Catatan

  • Asumsi: batas 800 update per detik per baris adalah angka ilustrasi. Ukur di database kamu.
  • Yang nggak dibahas: CRDT counter lintas wilayah dan counter dengan batas keras (kuota).
  • Angka Redis adalah orde besar, bergantung pada hardware, jaringan, dan ukuran payload.
  • Terakhir ditinjau: 26 Oktober 2026.

Isi · System Design