Rancang akses lintas partisi
Hitung biaya scatter/gather, pilih indeks sekunder lokal atau global, dan perkirakan fan-out query lintas partisi.
Pertanyaan
Apa yang terjadi pada query yang nggak memakai partition key, dan berapa biayanya dibandingkan alternatifnya?
Setelah baca ini, kamu bisa menghitung fan-out dan dampaknya pada latensi ekor, membandingkan indeks sekunder lokal dan global, dan memilih kapan scatter/gather masih bisa diterima.
Prasyarat dan contoh
Kamu sudah memilih partition key (M07.01), memahami hot key (M07.03), dan perutean (M07.04).
Tabel orders dipartisi dengan hash user_id di 32 partisi. Query "pesanan milik user 8841" mengenai satu partisi. Tapi dukungan pelanggan ingin mencari lewat order_id, dan admin ingin daftar pesanan berstatus pending. Dua query itu nggak membawa user_id, jadi router nggak tahu partisi mana yang menyimpannya.
Pilihannya: tanya semua partisi, atau bangun struktur tambahan yang menjawab dari lebih sedikit tempat.
Mekanisme
Scatter/gather. Router mengirim query ke semua N partisi (scatter), menunggu semua jawaban, lalu menggabungkan, mengurutkan ulang, dan memotong hasil (gather). Biayanya:
- Fan-out: satu query pengguna menjadi N request internal. Dengan 200 query per detik dan 32 partisi, itu 6.400 request per detik ke partisi, padahal query lewat key hanya 200.
- Latensi ekor: respons selesai saat partisi paling lambat selesai. Kalau 1% request ke satu partisi lambat (misalnya 50 ms ke atas), peluang minimal satu dari 32 partisi lambat adalah 1 - 0,99^32, sekitar 28%. Jadi p99 satu partisi menjadi sekitar p72 untuk query gabungan.
- Pagination: meminta halaman 100 dengan 20 hasil per halaman berarti tiap partisi harus mengembalikan 2.000 teratas supaya urutan global benar.
Indeks sekunder lokal (document-partitioned). Tiap partisi mengindeks hanya datanya sendiri. Tulisan murah, hanya ke satu partisi. Pembacaan lewat atribut sekunder tetap scatter/gather ke semua partisi.
Indeks sekunder global (term-partitioned). Indeks dipartisi sendiri dengan key atribut, misalnya status=pending atau order_id. Pembacaan hanya ke satu atau sedikit partisi indeks. Harganya di sisi tulis: satu tulisan pesanan sekarang menyentuh partisi data dan partisi indeks yang berbeda. Kalau pembaruan indeks dilakukan asinkron, indeks sedikit tertinggal (hasil query sedikit basi). Kalau sinkron, kamu butuh koordinasi lintas partisi, dengan latensi dan risiko kegagalan separuh jalan.
Denormalisasi. Alternatif ketiga: simpan salinan data dengan key lain. Tabel order_by_id dipartisi dengan order_id dan menyimpan lokasi (atau salinan) pesanan. Ini pada dasarnya indeks global yang kamu kelola sendiri, dengan tanggung jawab menjaga dua tabel selaras.
Transaksi yang menyentuh banyak partisi (misalnya memindahkan saldo antar dua akun di partisi berbeda) membutuhkan protokol komit atomik atau kompensasi. Itu di luar lesson ini.
Saat rancangan ini gagal
- Fan-out yang tumbuh bersama cluster. Menambah partisi dari 32 ke 128 membuat setiap scatter/gather 4 kali lebih berat, dan peluang partisi lambat naik: 1 - 0,99^128 sekitar 72%.
- Partisi yang tak menjawab. Satu partisi mati membuat seluruh query gagal atau mengembalikan hasil parsial. Putuskan apakah hasil parsial boleh dan tandai dengan jelas.
- Indeks global tertinggal. Pengguna membuat pesanan lalu mencarinya lewat
order_iddan hasilnya kosong. Jelaskan kontraknya, atau baca dari partisi data lewat key saat tahu key-nya. - Indeks global menjadi hot spot. Indeks
status=pendinghanya punya sedikit nilai, sehingga semua query dan tulisan mengarah ke sedikit partisi indeks. Kardinalitas rendah (M07.01) berlaku juga di indeks. - Urutan global yang mahal.
ORDER BY created_at LIMIT 20lintas partisi butuh penggabungan terurut dari semua partisi, dan offset yang dalam memaksa pembacaan jauh lebih banyak dari yang ditampilkan.
Latihan
orders dipartisi hash user_id di 32 partisi. Query pencarian bantuan lewat order_id: 150 per detik. Query admin status=pending: 6 per jam. Tiap partisi p99-nya 40 ms, dengan 1% request lebih lambat dari itu.
- Hitung request internal per detik dan peluang query lambat kalau memakai scatter/gather untuk pencarian
order_id. - Pilih solusi untuk tiap query dan sebutkan harganya.
Lihat pembahasan
- Fan-out 150 x 32 = 4.800 request internal per detik. Peluang minimal satu partisi lambat: 1 - 0,99^32 = 1 - 0,725, sekitar 27,5%. Jadi sekitar 1 dari 4 query pencarian melewati 40 ms, padahal p99 tiap partisi 40 ms.
order_id: buat tabel pencarianorder_by_idyang dipartisi hashorder_id, berisiuser_id(atau seluruh pesanan). Pencarian jadi satu hop ke partisi indeks, lalu satu hop ke partisi data bila perlu: 150 + 150 = 300 request per detik, bukan 4.800. Harga: setiap pembuatan pesanan menulis ke dua partisi, tabel bisa sedikit tertinggal sehingga pesanan yang baru dibuat mungkin belum ketemu, dan kamu harus menangani kegagalan separuh jalan (tulis tabel data dulu, lalu tabel pencarian dengan retry idempoten).status=pending: 6 per jam jauh terlalu jarang untuk membenarkan indeks. Pakai scatter/gather ke 32 partisi (192 request per jam) atau jalankan di replika baca. Harga: latensi lebih tinggi dan tak perlu ketat, dan hasil bisa parsial kalau satu partisi bermasalah.
Poin pentingnya: fan-out diterima atau ditolak berdasarkan frekuensi dan sensitivitas latensi, bukan berdasarkan "selalu hindari" atau "selalu boleh".
Terkait
- Sebelumnya: M07.05 Pindahkan data dengan aman.
- Kembali ke M07.01 Pilih partition key untuk melihat bagaimana pilihan key menentukan query mana yang murah.
- Latensi ekor dan p99 dibahas di M01.04 Tentukan sasaran yang berguna.
- Pencarian teks dan fan-out shard (M11) serta replikasi (M08) belum tersedia sebagai lesson. Lab L07, proyek B04, dan studi kasus Dynamo (C03) belum tersedia.
Catatan
- Asumsi: partisi independen dan lambatnya partisi nggak berkorelasi. Dalam praktik korelasi (misalnya compaction serentak) membuat hasil nyata berbeda dari perhitungan.
- Yang nggak dibahas: transaksi lintas partisi, join terdistribusi, dan pengurutan global dengan cursor.
- Perhitungan 1 - 0,99^N adalah model sederhana dengan asumsi independen.
- Terakhir ditinjau: 26 Oktober 2026.