Caching, CDN, dan Penyeimbangan Beban
Tambahkan lapisan caching Redis, dorong aset statis ke CDN, dan distribusikan lalu lintas ke seluruh replika dengan penyeimbang beban round-robin dan consistent-hashing
Caching, CDN, dan Penyeimbangan Beban adalah pelajaran Coding Interview Prep gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Coding Interview Prep, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Coding Interview Prep mencakup 4 pelajaran total.
Mengapa Tembolok Penting pada Skala Besar
Tembolok menyimpan salinan data yang sering diakses pada lapisan penyimpanan yang lebih cepat, sehingga permintaan berikutnya dapat dilayani tanpa mengakses penyimpanan pendukung yang lebih lambat (basis data, API eksternal). Pada skala besar, sejumlah kecil item populer menerima sebagian besar permintaan — aturan 80/20 (prinsip Pareto) sering berlaku: 20% item menyumbang 80% lalu lintas.
Tembolok yang dapat menampung 20% data terpopuler di memori mampu menyerap 80% beban basis data. Inilah alasan penambahan tembolok Redis sering mengurangi penggunaan CPU basis data sebesar 70–90% dan menurunkan latensi p99 dari 10 md menjadi kurang dari 1 md untuk data yang ditemukan di tembolok — tanpa perubahan besar pada basis data atau logika aplikasi.
# Demonstrating the 80/20 caching benefit
import random
# Simulate 1000 requests to 100 items with Zipf-like distribution
def zipf_sample(n_items, n_requests):
access_counts = {}
weights = [1.0 / (i + 1) for i in range(n_items)] # Zipf: item 0 most popular
total = sum(weights)
probs = [w / total for w in weights]
for _ in range(n_requests):
item = random.choices(range(n_items), weights=probs)[0]
access_counts[item] = access_counts.get(item, 0) + 1
return access_counts
random.seed(42)
counts = zipf_sample(100, 10000)
top_20_items = sorted(counts, key=counts.get, reverse=True)[:20]
top_20_requests = sum(counts[i] for i in top_20_items)
print(f'Top 20% of items ({20} of 100) handle {top_20_requests/100:.1f}% of requests')Pola Tembolok Terpisah (Pemuatan Malas)
Pola tembolok terpisah (juga disebut pemuatan malas) merupakan strategi penembolokan yang paling umum. Kode aplikasi bertanggung jawab mengelola tembolok: saat membaca, periksa tembolok terlebih dahulu. Jika tembolok ditemukan, segera kembalikan hasilnya. Jika tembolok tidak ditemukan, ambil data dari basis data, tulis data ke tembolok, lalu kembalikan hasilnya. Saat menulis, perbarui basis data dan batalkan validitas (delete) entri tembolok agar pembacaan berikutnya memuat ulang data tersebut.
Pola ini memastikan tembolok hanya menyimpan data yang benar-benar diminta (tanpa pemuatan awal yang tidak perlu) dan tetap konsisten dengan basis data melalui pembatalan validitas. Komprominya: akses pertama setelah tembolok tidak ditemukan harus menanggung seluruh biaya akses basis data (awal dingin).
# Cache-aside pattern in Python
class CacheAsideService:
def __init__(self, db, cache):
self.db = db
self.cache = cache # e.g., Redis client
def get_user(self, user_id):
cache_key = f'user:{user_id}'
# 1. Check cache
cached = self.cache.get(cache_key)
if cached:
return cached # cache hit
# 2. Cache miss: fetch from DB
user = self.db.query('SELECT * FROM users WHERE id=%s', user_id)
# 3. Write to cache with TTL
self.cache.set(cache_key, user, ttl=3600) # 1 hour TTL
return user
def update_user(self, user_id, data):
# 1. Write to DB
self.db.execute('UPDATE users SET ... WHERE id=%s', user_id, data)
# 2. Invalidate cache (delete, not update)
self.cache.delete(f'user:{user_id}')
# Next read will re-populate cache from DB
print('Cache-aside: READ from cache, miss? load from DB + write cache')
print(' WRITE to DB, then DELETE from cache (invalidate)')Penulisan Langsung dan Penulisan Tertunda pada Tembolok
Penulisan langsung: pada setiap penulisan, perbarui basis data dan tembolok secara sinkron. Tembolok selalu berisi data terbaru. Komprominya: penulisan menjadi lebih lambat (dua operasi), dan tembolok terisi data yang mungkin tidak akan dibaca lagi.
Penulisan tertunda (tulis balik): saat menulis, perbarui tembolok saja; kirim perubahan ke basis data secara asinkron nanti. Cara ini membuat penulisan sangat cepat, tetapi berisiko kehilangan data jika tembolok gagal sebelum perubahan dikirim. Cara ini digunakan pada beban kerja yang didominasi penulisan dan masih dapat menerima kehilangan sebagian data (misalnya, penghitung tayangan dan analitik).
# Write-through vs Write-behind comparison
strategies = {
'Cache-aside (Lazy)': {
'read': 'Check cache; miss => DB + populate cache',
'write': 'Write DB; delete from cache (invalidate)',
'consistency': 'Strong (invalidation ensures freshness)',
'write_latency': 'Fast (one DB write)',
'risk': 'Cache stampede on popular key expiry',
},
'Write-through': {
'read': 'Always check cache; miss => DB',
'write': 'Write DB AND cache atomically',
'consistency': 'Strong (cache always has latest)',
'write_latency': 'Slower (two writes per operation)',
'risk': 'Cache polluted with rarely-read data',
},
'Write-behind': {
'read': 'Check cache; miss => DB',
'write': 'Write cache only; async flush to DB',
'consistency': 'Eventual (flush may be delayed)',
'write_latency': 'Very fast (cache write only)',
'risk': 'Data loss if cache crashes before flush',
},
}
for name, info in strategies.items():
print(f'\n{name}:')
for k, v in info.items(): print(f' {k}: {v}')Kebijakan Penghapusan Tembolok
Ketika tembolok penuh, kebijakan penghapusan menentukan entri yang harus dihapus. Kebijakan yang paling umum:
- LRU (Paling Lama Tidak Digunakan): hapus entri yang paling lama tidak diakses. Bekerja baik untuk beban kerja dengan lokalitas temporal. Digunakan oleh Redis secara bawaan.
- LFU (Paling Jarang Digunakan): hapus entri yang paling sedikit kali diakses. Lebih baik untuk beban kerja dengan beberapa item yang selalu populer, tetapi popularitas tersebut tidak dapat ditangkap oleh LRU.
- FIFO: hapus entri yang paling lama dimasukkan. Sederhana, tetapi performanya buruk untuk beban kerja web pada umumnya.
- Acak: hapus entri secara acak. Dalam praktiknya, metode ini ternyata cukup kompetitif dibandingkan LRU pada tembolok yang sangat besar.
# Implementing LRU cache
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = OrderedDict() # maintains insertion/access order
def get(self, key):
if key not in self.cache:
return -1
self.cache.move_to_end(key) # mark as recently used
return self.cache[key]
def put(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False) # evict LRU (oldest)
cache = LRUCache(3)
for k, v in [('a',1),('b',2),('c',3)]:
cache.put(k, v)
print('Get a:', cache.get('a')) # 1 (a now most recently used)
cache.put('d', 4) # evicts 'b' (LRU)
print('Get b:', cache.get('b')) # -1 (evicted)
print('Get c:', cache.get('c')) # 3Jaringan Distribusi Konten (CDN)
CDN adalah jaringan peladen tepi yang tersebar secara geografis (titik kehadiran, PoPs) dan menyimpan konten statis serta dinamis dalam tembolok di dekat pengguna akhir. Alih-alih setiap permintaan pengguna harus dikirim ke peladen sumber di satu pusat data, simpul tepi CDN menyajikan konten dari PoP terdekat—mengurangi latensi dari sekitar 200 milidetik (antarbenua) menjadi sekitar 5 milidetik (PoP terdekat).
CDN sangat penting untuk: aset statis (gambar, CSS, JS), penyiaran video (segmen HLS), serta semakin banyak respons antarmuka pemrograman aplikasi dan HTML yang dibuat oleh peladen. CDN memeriksa tembolok tepinya; jika konten tidak ditemukan, CDN mengambilnya dari sumber dan menyimpannya dalam tembolok untuk permintaan berikutnya.
# CDN architecture flow
cdn_flow = [
'User requests https://example.com/image.jpg',
'DNS resolves to the nearest CDN PoP (e.g., Frankfurt for EU users)',
'CDN edge checks its local cache:',
' HIT: Return cached image directly (5ms latency)',
' MISS: Fetch from origin server (e.g., AWS S3 in us-east-1)',
' Cache image at edge with Cache-Control: max-age=86400',
' Future requests for this image served from edge (HIT)',
'Cache-Control headers control CDN behaviour:',
' max-age=31536000 s-maxage=31536000 -- cache 1 year',
' no-cache -- always revalidate',
' private -- CDN must not cache (user-specific)',
]
for step in cdn_flow:
print(step)
print('\nCDN providers: Cloudflare, AWS CloudFront, Fastly, Akamai')Penyeimbangan Beban: Mendistribusikan Lalu Lintas
Penyeimbang beban menyebarkan permintaan masuk ke beberapa peladen sisi belakang sehingga tidak ada satu peladen pun yang menjadi titik kemacetan. Penyeimbang beban juga menyediakan ketersediaan tinggi: jika salah satu peladen gagal, penyeimbang beban secara otomatis mengarahkan lalu lintas ke peladen yang sehat (pemeriksaan kesehatan setiap 5–30 detik).
Penyeimbang beban beroperasi pada berbagai lapisan OSI: Lapisan 4 (transportasi—mengatur rute berdasarkan IP/porta, sangat cepat) dan Lapisan 7 (aplikasi—mengatur rute berdasarkan jalur alamat, tajuk, dan kuki, sehingga memungkinkan pengaturan rute yang lebih cerdas). AWS ALB, Nginx, dan HAProxy adalah penyeimbang beban Lapisan 7 yang umum. AWS NLB adalah penyeimbang beban Lapisan 4.
# Load balancing algorithms
algorithms = {
'Round Robin': {
'how': 'Rotate through servers in sequence',
'best_for': 'Stateless servers with similar capacity',
'weakness': 'Does not account for server load or response time',
},
'Weighted Round Robin': {
'how': 'Round robin but servers with more capacity get more requests',
'best_for': 'Heterogeneous server fleet',
'weakness': 'Static weights; does not adapt to runtime load',
},
'Least Connections': {
'how': 'Send to server with fewest active connections',
'best_for': 'Long-lived connections (WebSocket, streaming)',
'weakness': 'More complex tracking of connection state',
},
'Consistent Hashing': {
'how': 'Hash request key (user_id, session) to server',
'best_for': 'Sticky sessions, cache locality per server',
'weakness': 'Uneven distribution if hash space is not balanced',
},
'Random': {
'how': 'Choose server at random',
'best_for': 'Simple stateless workloads',
'weakness': 'No guarantee of load balance in short windows',
},
}
for alg, info in algorithms.items():
print(f'{alg}: {info["how"]}')Pencincangan Konsisten: Menambah dan Menghapus Simpul
Pencincangan konsisten mengatasi masalah pendistribusian ulang kunci tembolok ketika peladen ditambahkan atau dihapus. Pada pencincangan modulo sederhana (server = hash(key) % n), perubahan nilai n memetakan ulang hampir semua kunci sehingga menyebabkan cache stampede. Pencincangan konsisten memetakan kunci dan peladen ke sebuah cincin; setiap kunci dilayani oleh peladen terdekat searah jarum jam. Penambahan satu peladen hanya memetakan ulang kunci di antara peladen baru dan pendahulunya—sekitar 1/n dari seluruh kunci.
Simpul virtual meningkatkan pemerataan beban: setiap peladen fisik diberi beberapa posisi pada cincin, sehingga kunci tersebar lebih merata meskipun jumlah peladen sedikit.
import hashlib
import bisect
class ConsistentHashRing:
def __init__(self, replicas=100):
self.replicas = replicas # virtual nodes per server
self.ring = {}
self.sorted_keys = []
def add_server(self, server):
for i in range(self.replicas):
key = int(hashlib.md5(f'{server}:{i}'.encode()).hexdigest(), 16)
self.ring[key] = server
bisect.insort(self.sorted_keys, key)
def remove_server(self, server):
for i in range(self.replicas):
key = int(hashlib.md5(f'{server}:{i}'.encode()).hexdigest(), 16)
del self.ring[key]
self.sorted_keys.remove(key)
def get_server(self, item):
key = int(hashlib.md5(item.encode()).hexdigest(), 16)
idx = bisect.bisect(self.sorted_keys, key) % len(self.sorted_keys)
return self.ring[self.sorted_keys[idx]]
ring = ConsistentHashRing()
for s in ['server-1', 'server-2', 'server-3']:
ring.add_server(s)
for item in ['user:1', 'user:2', 'product:abc', 'session:xyz']:
print(f'{item} => {ring.get_server(item)}')Cache Stampede dan Solusinya
Tembolok stampede (atau kawanan yang menggelegar) terjadi ketika entri tembolok yang populer kedaluwarsa dan banyak permintaan bersamaan tidak menemukan entri tersebut pada saat yang sama, sehingga membanjiri basis data dengan kueri yang sama. Solusinya:
- Saling eksklusi/penguncian: hanya satu permintaan yang menghitung nilainya; permintaan lain menunggu
- Kedaluwarsa awal probabilistik: sesaat sebelum TTL berakhir, sebuah permintaan secara acak memutuskan untuk menyegarkan tembolok sehingga kedaluwarsa bersamaan dapat dicegah
- Penyajian data lama sambil memvalidasi ulang: sajikan konten lama segera sambil menyegarkan tembolok secara asinkron
- Penyegaran di latar belakang: proses terpisah menyegarkan kunci populer sebelum kedaluwarsa
import time, threading, random
# Probabilistic early expiry (XFetch algorithm)
class ProbabilisticCache:
def __init__(self):
self._cache = {}
def get(self, key, ttl, recompute_fn, beta=1.0):
if key in self._cache:
value, expiry, delta = self._cache[key]
# XFetch: decide to refresh early with probability proportional to delta/TTL
remaining = expiry - time.time()
if remaining > 0:
early_refresh_score = delta * beta * (-1) * (remaining / ttl)
if random.random() > (1 - early_refresh_score): # simplified
pass # could trigger async refresh here
return value
# Cache miss or expired
start = time.time()
value = recompute_fn()
delta = time.time() - start # computation time
expiry = time.time() + ttl
self._cache[key] = (value, expiry, delta)
return value
print('XFetch: refresh probabilistically before expiry based on computation cost')
print('High-cost computations => refresh earlier to avoid stampede')
print('Low-cost computations => refresh closer to TTL')Invalidasi Tembolok CDN
Invalidasi tembolok terkenal sulit: “Hanya ada dua masalah sulit dalam ilmu komputer: invalidasi tembolok dan penamaan sesuatu.” Ketika konten berubah di sumber, simpul tepi CDN harus menyajikan versi baru. Strateginya:
- Kedaluwarsa berdasarkan TTL: biarkan konten kedaluwarsa secara alami (sederhana, tetapi memiliki jendela data lama)
- Pemberian versi alamat: sematkan nilai cincang konten dalam alamat (misalnya,
main.a3f2b.js); konten baru = alamat baru, sehingga tidak diperlukan invalidasi - Penghapusan melalui antarmuka pemrograman aplikasi CDN: hapus alamat secara eksplisit melalui panggilan antarmuka pemrograman aplikasi setelah penerapan (cepat, tetapi memerlukan integrasi dengan antarmuka pemrograman aplikasi CDN)
# Cache invalidation strategies for CDN/browser
strategies = [
{
'name': 'Long TTL + URL versioning (best for static assets)',
'example': '<script src="/app.a3f2b1c.js"></script>',
'ttl': 'Cache-Control: max-age=31536000 (1 year)',
'how': 'Content hash in filename; new deploy = new URL; old URL cached forever (OK)',
},
{
'name': 'Short TTL (for frequently changing content)',
'example': '/api/v1/config',
'ttl': 'Cache-Control: max-age=60 (1 minute)',
'how': 'Simple; content is at most 60s stale; no invalidation needed',
},
{
'name': 'CDN API purge (for news / social media)',
'example': '/news/breaking-story.html',
'ttl': 'Cache-Control: s-maxage=3600',
'how': 'On publish, call CDN.purge(url); edge serves new version immediately',
},
]
for s in strategies:
print(f'{s["name"]}:')
print(f' Example: {s["example"]}')
print(f' TTL: {s["ttl"]}')
print(f' Strategy: {s["how"]}\n')Arsitektur: Menggabungkan Semuanya
Lapisan aplikasi web yang sepenuhnya diskalakan menggunakan ketiga teknik secara bersamaan: penyeimbangan beban mendistribusikan lalu lintas, CDN menyerap permintaan aset statis dan antarmuka pemrograman aplikasi yang dapat disimpan dalam tembolok, sedangkan Redis menyimpan data dinamis dalam tembolok. Basis data hanya menerima permintaan yang tidak ditemukan di tembolok—biasanya 5–20% dari seluruh permintaan.
Alur permintaan yang umum untuk antarmuka pemrograman aplikasi yang didominasi pembacaan: pengguna → DNS → tepi CDN (ditemukan dalam tembolok: langsung disajikan) → tidak ditemukan di CDN → penyeimbang beban → kumpulan peladen aplikasi → tembolok Redis (ditemukan: respons 1 milidetik) → tidak ditemukan di Redis → basis data (10–50 milidetik) → respons disimpan dalam tembolok Redis + CDN opsional → pengguna. Setiap lapisan secara signifikan mengurangi beban basis data.
# Request flow with cache hit rates
request_flow = [
('Browser Cache', '10%', '0ms', 'Browser caches GET responses per Cache-Control'),
('CDN Edge Cache', '60%', '5ms', 'CloudFront/Fastly caches cacheable API responses'),
('Load Balancer', None, '1ms', 'Routes to healthy app server replica'),
('App Server', None, '2ms', 'Business logic, auth check'),
('Redis Cache', '25%', '1ms', 'Caches computed data, hot DB rows'),
('Database Read Replica','5%', '10ms', 'Cache miss: query read replica'),
('Database Primary', '0.1%', '15ms', 'Cache+replica miss: query primary (rare for reads)'),
]
print(f'{'Layer':30s} {'Hit Rate':10s} {'Latency':10s} {'Notes'}')
print('-'*80)
for layer, hit_rate, latency, note in request_flow:
hr = hit_rate if hit_rate else '-'
print(f'{layer:30s} {hr:10s} {latency:10s} {note}')
print('\nResult: DB sees ~5% of requests; Redis sees ~25%; CDN absorbs 60%; browser 10%')Kiat Wawancara: Tembolok dan Penyeimbangan Beban
Saat membahas tembolok dalam wawancara rancangan sistem, selalu bahas: apa yang perlu disimpan dalam tembolok (data yang sering digunakan, perhitungan yang mahal), di mana data disimpan dalam tembolok (peramban, CDN, aplikasi, tembolok kueri basis data), kapan tembolok diinvalidasi (saat penulisan, saat TTL kedaluwarsa, atau melalui penyegaran di latar belakang), dan jaminan konsistensi apa yang dapat diterima. Tembolok menimbulkan jendela konsistensi—jelaskan hal ini secara tegas.
Untuk penyeimbangan beban, sebutkan pilihan algoritme, pemeriksaan kesehatan, sesi tetap jika diperlukan, dan apakah penskalaan horizontal peladen aplikasi tanpa status memungkinkan. Jika aplikasi memiliki status (koneksi WebSocket, sesi), jelaskan cara status tersebut dikelola di seluruh replika.
# Caching design questions checklist
cache_checklist = [
'What data to cache? (read-heavy, expensive to compute, rarely updated)',
'Cache layer: client-side / CDN / app-level / DB query cache?',
'Cache invalidation strategy: TTL / event-driven / write-through?',
'Eviction policy: LRU / LFU?',
'Cache key design: ensure uniqueness, avoid hotspots',
'Consistency window: acceptable staleness in seconds?',
'Cache stampede prevention: mutex / stale-while-revalidate?',
'Cache capacity: how much RAM needed for hot set?',
]
lb_checklist = [
'Layer 4 vs Layer 7: routing by IP or by URL/headers?',
'Algorithm: round robin / least-connections / consistent hashing?',
'Health checks: interval, failure threshold, recovery',
'Session stickiness: needed? Use cookie-based affinity or external session store',
'Auto-scaling: scale out when CPU > 70%; scale in when < 30%',
]
print('Cache checklist:')
for item in cache_checklist: print(f' [ ] {item}')
print('\nLoad balancer checklist:')
for item in lb_checklist: print(f' [ ] {item}')Pemeriksaan Singkat
Uji pemahaman Anda tentang konsep Struktur Data & Algoritma—Persiapan Wawancara Pemrograman dari pelajaran ini.
Ringkasan Pelajaran
Dalam pelajaran ini Anda mempelajari bahwa: tembolok menyimpan data yang sering digunakan dalam lapisan memori cepat (Redis, CDN) untuk menangani sebagian besar pembacaan dan mengurangi beban basis data, pola tembolok saat diperlukan adalah pola yang paling umum—ketika data tidak ditemukan, data dimuat dari basis data; ketika ditemukan, data langsung dikembalikan; saat menulis, data dihapus dari tembolok, dan pencincangan konsisten mendistribusikan kunci tembolok ke seluruh simpul sehingga penambahan atau penghapusan simpul hanya memetakan ulang sekitar 1/n kunci, bukan semuanya. Selanjutnya, kita akan merancang pembatas laju dan umpan Twitter untuk menerapkan semua konsep rancangan sistem dalam masalah menyeluruh.
Belajar Coding Interview Prep dengan tutor AI — gratis
Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.
- Kursus
- 90
- Pelajaran
- 360
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Caching, CDN, dan Penyeimbangan Beban” gratis?
Ya — teks lengkap “Caching, CDN, dan Penyeimbangan Beban” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Coding Interview Prep, upgrade ke CoddyKit PRO. Kursus Coding Interview Prep mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Caching, CDN, dan Penyeimbangan Beban”?
Tambahkan lapisan caching Redis, dorong aset statis ke CDN, dan distribusikan lalu lintas ke seluruh replika dengan penyeimbang beban round-robin dan consistent-hashing Kamu berlatih Coding Interview Prep dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Coding Interview Prep?
Tidak diperlukan pengalaman sebelumnya. Coding Interview Prep di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.
Berapa lama pelajaran “Caching, CDN, dan Penyeimbangan Beban” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Coding Interview Prep ini?
Ya. Setiap pelajaran Coding Interview Prep menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Kerangka Wawancara Desain Sistem
- Penyimpanan Data Skalabel: SQL vs NoSQL
- Caching, CDN, dan Penyeimbangan Beban
- Merancang Pembatas Laju dan Merancang Feed Twitter