Kesh invalidatsiyasi strategiyalari: TTL, write-through, stampede
Chuqur4 daqInnovialabs tahririyati
Qisqacha
Ko‘pchilik tizimlar uchun TTL’li cache-aside va har bir yozuvda kalitni o‘chirish mos keladi; issiq kalitlarni lock yoki single-flight bilan himoyalang, TTL’ga jitter qo‘shing va fonda yangilanayotganda eskirgan ma’lumotni bering.
Qisqacha javob
Mukammal invalidatsiya strategiyasi yo‘q — ma’lumotlar yangiligi, bazaga yuklama va murakkablik o‘rtasida murosa bor. Ko‘pchilik mahsulotlar uchun ishonchli standart variant:
- cache-aside sxemasida o‘qish: keshni tekshiramiz, topilmasa bazadan olib, natijani saqlaymiz;
- har bir kalitda sug‘urta sifatida TTL;
- haqiqat manbasiga har bir yozuvdan keyin kalitni o‘chirish;
- issiq kalitlarni stampede’dan himoya qilish.
Har bir ma’lumot turi uchun qanchalik eskirishi mumkinligini hal qilishdan boshlang. Mahsulot tavsifi bir daqiqa kechikishi mumkin, buyurtma rasmiylashtirishdagi balans yoki ombordagi qoldiq esa — yo‘q.
Yozish sxemalarini taqqoslash
| Sxema | Qanday ishlaydi | Qayerda yaxshi | Risklar |
|---|---|---|---|
| Cache-aside | Ilova keshni o‘qiydi, topilmasa to‘ldiradi, yozishda invalidatsiya qiladi | O‘qish ustun bo‘lgan ko‘pchilik API | Yozish va qayta to‘ldirish o‘rtasida poyga |
| Write-through | Har bir yozuv kesh va bazaga birga boradi | Yozilgandan so‘ng darhol o‘qiladigan ma’lumotlar | Sekinroq yozish, kesh kam o‘qiladigan narsalar bilan to‘ladi |
| Write-behind | Keshga yozish, bazaga — asinxron | Juda tez-tez yozuvlar, hisoblagichlar | Kesh bazaga yozishdan oldin tushib qolsa, ma’lumot yo‘qoladi |
Write-behind eng xavflisi: kesh vaqtincha haqiqat manbasiga aylanadi. Undan oxirgi yozuvlarni yo‘qotish maqbul bo‘lgan yoki navbat ishonchli bo‘lgan joyda foydalaning.
TTL va hodisaga asoslangan invalidatsiya
TTL oddiy va o‘zini o‘zi tiklaydi: invalidatsiya qilishni unutsangiz ham, muddat tugagach ma’lumot to‘g‘ri bo‘ladi. Narxi — kafolatlangan eskirish oynasi va davriy keshga tushmaslik (miss).
Hodisaga asoslangan invalidatsiya ma’lumot o‘zgarganda kalitlarni o‘chiradi yoki yangilaydi: yozuvdan keyin darhol, xabarlar shinasi orqali yoki bazaning change data capture (CDC) orqali. Ma’lumot yangiroq, lekin yozuvga bog‘liq barcha kalitlarni bilish kerak, yo‘qolgan hodisa esa biror narsa tuzatmaguncha eskirgan ma’lumotni anglatadi.
Amalda ularni birlashtiradi: hodisalar yangilikni beradi, TTL o‘tkazib yuborilgan hodisalardan sug‘urtalaydi.
Amaliy qoidalar:
- Yozishda kalitni qayta yozmang, o‘chiring. Keshni yangilayotgan ikki parallel yozuvchi unda eskiroq qiymatni qoldirishi mumkin.
- Avval bazaga yozing, so‘ng kalitni o‘chiring.
- O‘quvchi o‘chirilgandan keyin darhol eski qiymatni qaytarib qo‘yishi mumkin. Qisqa TTL, kechiktirilgan ikkinchi o‘chirish yoki versiyalangan kalitlar (
product:42:v17) bu bo‘shliqni yopadi. - «Topilmadi» natijasini qisqa muddatga keshlang, shunda mavjud bo‘lmagan ID’lar uchun so‘rovlar bazaga urilmaydi.
Cache stampede va uni qanday to‘xtatish
Stampede mashhur kalit muddati tugaganda va yuzlab so‘rovlar bir vaqtda keshga tushmay, bir xil og‘ir so‘rovni bajarganda yuz beradi. Baza sekinlashadi, ko‘proq so‘rovlar taymautga tushadi va muammo o‘zini o‘zi kuchaytiradi.
Ishlaydigan usullar:
- Lock yoki single-flight: qiymatni faqat bitta so‘rov qayta yig‘adi, qolganlari qisqa kutadi yoki eski qiymatni oladi.
- TTL uchun jitter: muddatga tasodifiy tarqoqlik qo‘shing, shunda birga yaratilgan kalitlar birga tugamaydi.
- Stale-while-revalidate: bitta fon vazifasi yangilayotgan paytda muddati o‘tgan qiymatni berishda davom eting.
- Erta yangilash: issiq kalitlarni muddati tugashidan sal oldin yangilang — jadval bo‘yicha yoki o‘qishda ehtimollik asosida.
- Deploy yoki kesh qayta ishga tushgandan keyin ma’lum issiq kalitlarni qizdirish.
Redis’da minimal lock:
def get_product(pid):
key = f"product:{pid}"
cached = redis.get(key)
if cached:
return json.loads(cached)
if redis.set(f"lock:{key}", "1", nx=True, ex=10):
try:
product = db.load_product(pid)
ttl = 300 + random.randint(0, 60) # jitter
redis.set(key, json.dumps(product), ex=ttl)
return product
finally:
redis.delete(f"lock:{key}")
time.sleep(0.05)
cached = redis.get(key)
return json.loads(cached) if cached else db.load_product(pid)
HTTP javoblari uchun xuddi shu g‘oya Cache-Control sarlavhasiga o‘rnatilgan: max-age=60, stale-while-revalidate=300 CDN va brauzerlarga yangi nusxa yuklanayotganda biroz eskirgan nusxani berishga imkon beradi.
Qanday tanlash kerak
- Ma’lumotlarni ruxsat etilgan eskirish bo‘yicha ajrating: soniyalar, daqiqalar, soatlar, hech qachon.
- Eskirishi mumkin bo‘lmagan ma’lumotlar (pul, rasmiylashtirishdagi qoldiq) bazadan o‘qiladi yoki u bilan solishtiriladi; kesh — faqat ishora.
- Qolganlari uchun — TTL va yozishda o‘chirish bilan cache-aside.
- Hodisalarni faqat TTL bo‘yicha eskirish foydalanuvchilarga sezilarli xalaqit beradigan joyda qo‘shing.
- Hit rate, miss paytidagi kechikish va bazaga yuklamani kuzating. Relizdan keyin hit rate’ning tushishi ko‘pincha kalit formati o‘zgarganini bildiradi.