Tizim dizayni kursi · 4-mavzu
TuzdiDinMuhammad

Caching — tezlikning birinchi quroli

Tez-tez so‘raladigan ma’lumotni tez xotirada saqlab, bazadagi yukni o‘nlab marta kamaytiramiz. Noldan boshlab hit/miss, kesh strategiyalari, Redis bilan NestJS implementatsiyasi, eng qiyin qism — invalidatsiya va klassik tuzoqlar (stampede, avalanche, penetration)ni o‘rganamiz.

Stack: NestJS · Redis · cache-managerNaqshlar: Cache-Aside · write-through · TTLBog‘liq: 3-mavzu (Event-Driven)

Bu darsda nima bor

1–4-qismlar keshni noldan quradi: nega kerak, hit/miss va qatlamlar, strategiyalar va Redis bilan NestJS implementatsiya. 5–6-qismlar — eng muhim production muammolari: invalidatsiya va klassik tuzoqlar. 7-qism nimani keshlash va qachon ishlatmaslik kerakligini hal qiladi.

01
Caching · qism 1

Nega kerak — bazaning tor bo‘g‘izi

🗄️

Oddiy o‘xshatish: tez-tez kerak bo‘ladigan hujjatni har safar arxivga (omborga) borib olib kelmaysiz — uni stol ustida, qo‘l ostida saqlaysiz. Bir necha soniya o‘rniga — bir zumda.

Bosh sahifangizdagi "eng ko‘p sotilganlar" ro‘yxatini olaylik. Unga sekundiga 5000 odam kiradi va har biri uchun baza bir xil og‘ir JOINli so‘rovni qayta-qayta bajaradi.

Muammo

Har bir so‘rovda ma’lumotlar bazasiga murojaat qilish sekin va qimmat. Ma’lumot kam o‘zgarsa ham, baza bir xil natijani minglab marta hisoblaydi. Yuk ortganda baza tor bo‘g‘iz (bottleneck)ga aylanadi: javoblar sekinlashadi, ulanishlar tugaydi va butun tizim cho‘kadi. Bazani kuchaytirish — eng qimmat yechim.

Yechim — keshlash

Tez-tez so‘raladigan ma’lumotni tez xotirada (Redis, ilova xotirasi, CDN) saqlash. So‘rov kelganda avval keshdan qaraladi: bo‘lsa — darhol qaytariladi (cache hit, ~1ms); bo‘lmasa — bazadan olinadi, keshga yoziladi va keyingi safarga tayyor turadi (cache miss).

Nima uchun ishlaydi — 80/20

Real tizimlarda so‘rovlarning katta qismi kichik to‘plamdagi mashhur ma’lumotga tushadi (mashhur mahsulot, bosh sahifa, sessiya). Shu "qaynoq" 20%ni keshlasa, bazadagi yukning 80%i yo‘qoladi.

02
Caching · qism 2

Asosiy g‘oya — hit/miss va kesh qatlamlari

Keshning ikki holati bor va butun mavzu shu ikkitasi atrofida aylanadi:

  • Cache HIT — so‘ralgan ma’lumot keshda bor → darhol qaytariladi (bazaga bormaydi).
  • Cache MISS — keshda yo‘q → bazadan olinadi, keshga yoziladi, qaytariladi.

Keshning samarasi hit rate (necha % so‘rov keshdan javob oldi) bilan o‘lchanadi. 90% hit rate = bazaga atigi 10% so‘rov tushadi.

Ilova 1. keshdan so‘ra HIT → ~1ms Kesh (Redis)RAM MISS bo‘lsa Ma’lumotlarbazasi
Avval keshdan (tez), bo‘lmasa bazadan — keyin keshga yoziladi

Kesh qayerda yashaydi — bir necha qatlam

Kesh bitta joy emas — foydalanuvchidan bazagacha bir necha bosqichda bo‘lishi mumkin. Har biri bazaga yetib borishni kamaytiradi:

QatlamJoyiMisol
BrauzerMijoz qurilmasidaHTTP Cache-Control, rasm/CSS
CDNFoydalanuvchiga yaqin chekka serverlarStatik fayllar, rasm, video
Ilova ichi (in-memory)Server jarayoni xotirasiEng tez, lekin har nusxada alohida
Taqsimlangan keshAlohida server (Redis)Hamma nusxalar uchun umumiy
In-memory vs Redis

Ilova xotirasidagi kesh eng tez, lekin har bir server nusxasida alohida bo‘ladi (5 ta nusxa = 5 ta turli kesh, izchillik yo‘q). Redis — barcha nusxalar uchun yagona, izchil kesh. Production’da odatda Redis tanlanadi.

03
Caching · qism 3

Strategiyalar — Cache-Aside, write-through/behind

Kesh va baza qanday muvofiqlashadi — bir necha naqsh bor. Eng muhim 4 tasi:

1 · Cache-Aside (lazy loading) — eng keng tarqalgan

Ilova keshni o‘zi boshqaradi: avval keshdan so‘raydi, miss bo‘lsa bazadan olib, keshga yozadi. Kesh "yon"da turadi (aside).

Ilova 1. get (miss) Kesh 2. bazadan o‘qi Baza 3. keshga yoz
Ilova boshqaradi: keshda yo‘q bo‘lsa, bazadan olib keshga joylaydi

Yozish strategiyalari — Write-through va Write-behind

StrategiyaQandayKelishuv
Write-throughYozuvda ham keshga, ham bazaga birga yoziladiKesh doim yangi; lekin yozuv biroz sekinroq
Write-behind
(write-back)
Avval keshga, bazaga keyinroq (asinxron)Yozuv juda tez; lekin server qulasa ma’lumot yo‘qolishi mumkin
Read-throughMiss’da keshning o‘zi bazadan oladi (ilova emas)Ilova kodi soddalashadi; kutubxona/kesh qatlami boshqaradi
Amaliy tanlov

Aksariyat holatlarda Cache-Aside + TTL yetarli va eng oddiy — shundan boshlang. Write-through izchillik muhim bo‘lganda, write-behind esa yozuv juda ko‘p va tezlik kritik bo‘lganda ishlatiladi.

04
Caching · qism 4

NestJS implementatsiya (Redis)

@nestjs/cache-manager

NestJS keshni @nestjs/cache-manager orqali beradi. Ikki usul bor: avtomatik (interceptor) va qo‘lda (to‘liq nazorat). Production’da odatda qo‘lda Cache-Aside ko‘proq ishlatiladi.

1 · Kesh modulini ulash (Redis)

// app.module.ts — Redis bilan kesh modulini ulaymiz
import { CacheModule } from '@nestjs/cache-manager';
import { redisStore } from 'cache-manager-redis-yet';

@Module({
  imports: [
    CacheModule.registerAsync({
      isGlobal: true,
      useFactory: async () => ({
        store: await redisStore({ url: 'redis://localhost:6379' }),
        ttl: 60_000,   // DIQQAT: cache-manager v5 da TTL = millisekund
      }),
    }),
  ],
})
export class AppModule {}

2 · Qo‘lda Cache-Aside — to‘liq nazorat

// product.service.ts — qo'lda Cache-Aside (eng ko'p ishlatiladigan naqsh)
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';

@Injectable()
export class ProductService {
  constructor(
    @Inject(CACHE_MANAGER) private cache: Cache,
    private repo: ProductRepository,
  ) {}

  async getById(id: string): Promise<Product> {
    const key = `product:${id}`;
    const cached = await this.cache.get<Product>(key);
    if (cached) return cached;                       // HIT -> darhol

    const product = await this.repo.findById(id);    // MISS -> bazadan
    await this.cache.set(key, product, 60_000);      // keshga yoz (60s)
    return product;
  }

  async update(id: string, dto: UpdateProductDto) {
    const product = await this.repo.update(id, dto);
    await this.cache.del(`product:${id}`);           // INVALIDATE (eskisini o'chir)
    return product;
  }
}

3 · Avtomatik keshlash — interceptor

Oddiy, kam o‘zgaradigan GET endpointlar uchun qulay — kod yozmasdan keshlaydi:

// Avtomatik HTTP keshlash — interceptor (oddiy GET endpointlar uchun)
import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager';

@Controller('products')
@UseInterceptors(CacheInterceptor)
export class ProductController {
  @Get()
  @CacheKey('products_all')
  @CacheTTL(30_000)          // 30s
  findAll() { return this.service.findAll(); }
}
Eslatma — versiya

Redis store paketi (cache-manager-redis-yet / @keyv/redis) va TTL birligi (ms yoki sek) cache-manager versiyasiga qarab farq qiladi. v5+ da TTL — millisekund. Loyihangiz versiyasini tekshiring.

05
Chuqurlashtirilgan mavzu

Invalidatsiya — eng qiyin muammo

TTL · write · event
⚠️

Kompyuter fanida mashhur hazil bor: "Faqat ikkita qiyin narsa bor — kesh invalidatsiyasi va narsalarga nom berish." Invalidatsiya — keshning eng og‘riqli muammosi.

Muammo — eskirgan ma’lumot (stale data)

Mahsulot narxi bazada o‘zgardi, lekin keshda eski narx turibdi. Foydalanuvchi noto‘g‘ri narx ko‘radi. Savol: ma’lumot o‘zgarganda keshdagi eski nusxani qachon va qanday yangilash kerak?

3 ta yondashuv

1 · TTL (Time To Live) — eng oddiy. Har keshga yashash muddati beriladi (masalan 60s). Muddat tugagach o‘zi o‘chadi va keyingi so‘rovda yangilanadi. Kelishuv: TTL davomida ma’lumot biroz eskirgan bo‘lishi mumkin. Ko‘p hollarda — joiz.

2 · Yozuvda invalidatsiya (write invalidation). Ma’lumot o‘zgarganda keshdagi kalitni darhol del qilasan (yuqoridagi update misolidagidek). Eng aniq, lekin har o‘zgarish joyida keshni o‘chirishni eslab turish kerak.

3 · Hodisaga asoslangan invalidatsiya. O‘zgarishda event chiqadi, keshga bog‘liq joylar o‘zini tozalaydi — bo‘shroq bog‘langan:

// Hodisaga asoslangan invalidatsiya (3-mavzu bilan bog'lanadi)
// Mahsulot o'zgarganda event chiqadi -> keshlar o'zini tozalaydi
@OnEvent('product.updated')
async onProductUpdated(e: ProductUpdated) {
  await this.cache.del(`product:${e.id}`);
  await this.cache.del('products_all');     // bog'liq ro'yxat keshini ham
}
update() 1. bazaga yoz Baza 2. cache.del(key) — eskisini o‘chir
Yozuvda keshni o‘chirish → keyingi o‘qishda yangisi keladi
Oltin qoida

Mukammal izchillik kerak bo‘lsa — TTL’ni qisqa qil yoki yozuvda invalidatsiya qil. Biroz eskirish joiz bo‘lsa — uzunroq TTL ishlat. Aniq izchillik va tezlik o‘rtasidagi kelishuvni biznes ehtiyoji hal qiladi.

06
Chuqurlashtirilgan mavzu

Klassik muammolar va eviction

stampede · avalanche · penetration

Keshda 3 ta klassik muammo bor — ularni bilmaslik tizimni yiqitishi mumkin:

1 · Cache Stampede (thundering herd)

Mashhur kalit keshdan o‘chdi (TTL tugadi). Ayni o‘sha lahzada minglab so‘rov birato‘la miss bo‘lib, hammasi bazaga yuguradi — baza to‘fondan cho‘kadi.

so‘rovso‘rovso‘rov Baza 🔥hammasi birdan
Kesh bo‘shashi → bir vaqtda baza to‘foni

Yechim (qisqacha): faqat bitta so‘rov bazaga borib keshni to‘ldirsin (lock / "single-flight"), qolganlar kutsin; yoki muddat tugashidan oldin fonda yangilash. (Chuqur mexanika — Deep qatlamda.)

2 · Cache Avalanche (qor ko‘chkisi)

Ko‘p kalitga bir xil TTL berilgan → hammasi bir vaqtda tugaydi → bir zumda ulkan miss to‘lqini. Yechim: TTL’ga tasodifiy qo‘shimcha (jitter) ber: 60s + random(0..10s) — muddatlar tarqaladi.

3 · Cache Penetration (teshib o‘tish)

So‘rovlar umuman mavjud bo‘lmagan kalitga tushadi (masalan, product:-1). Kesh doim miss, har safar baza benihoya so‘raladi (hatto buzg‘unchi shu orqali bazani yuklashi mumkin). Yechim: "yo‘q" natijani ham qisqa TTL bilan keshla (negative caching).

Eviction — kesh to‘lganda nima o‘chadi

Kesh xotirasi cheksiz emas. To‘lganda eskilarini chiqarib tashlaydi (eviction). Asosiy siyosatlar: LRU (eng kam yaqinda ishlatilgani), LFU (eng kam tez-tez ishlatilgani), TTL (muddati bo‘yicha). Redis’da bu maxmemory-policy bilan sozlanadi.

07
Qaror

Nimani keshlash va qachon ishlatmaslik

Kesh kuchli, lekin har joyda emas. Noto‘g‘ri ishlatilsa — eskirgan ma’lumot va topish qiyin xatolar keltiradi.

Keshlash YAXSHI bo‘lganda
  • Ma’lumot ko‘p o‘qiladi, kam o‘zgaradi
  • Biroz eskirish joiz (lenta, katalog, profil)
  • Hisoblash yoki so‘rov qimmat (og‘ir JOIN, agregatsiya)
  • Bir xil natija qayta-qayta so‘raladi (mashhur kalitlar)
EHTIYOT bo‘ling / keshlamang
  • Ma’lumot har soniyada o‘zgaradi (real-time narx)
  • Mutlaq aniqlik shart (hisob qoldig‘i, to‘lov)
  • Har foydalanuvchi uchun noyob, qayta so‘ralmaydigan
  • Yozuv o‘qishdan ko‘p (kesh foyda bermaydi)
Amaliy qoidalar

1. Avval o‘lchang — qaysi so‘rov sekin/ko‘p? Faqat o‘shani keshlang, hammasini emas.

2. Hit rateni kuzating. Past hit rate = kesh foyda bermayapti, balki noto‘g‘ri kalit yoki juda qisqa TTL.

3. Kalit nomlanishini izchil qiling: product:42, user:7:orders — versiyalash oson bo‘lsin.

4. Kesh ishonchlilik manbai emas — u o‘chsa, tizim (sekinroq bo‘lsa-da) ishlashda davom etishi kerak. Keshga "majburiy" tayanmang.

08
Yakuniy

Eslab qolish kerak bo‘lgan 5 jumla

  • Kesh = tez-tez o‘qiladigan ma’lumotni tez xotirada saqlash. HIT (~1ms) bazaga bormaydi; MISS bazadan olib keshga yozadi.
  • Eng ko‘p ishlatiladigan naqsh — Cache-Aside + TTL. Shundan boshla; write-through/behind keyin kerak bo‘lsa.
  • Invalidatsiya — eng qiyin qism. TTL (oddiy), yozuvda del (aniq), yoki hodisaga asoslangan. Tezlik va izchillik orasidagi kelishuv.
  • 3 ta tuzoq: stampede (lock/single-flight), avalanche (TTL jitter), penetration (negative caching). Kesh to‘lsa — LRU/LFU eviction.
  • Faqat ko‘p o‘qiladigan, kam o‘zgaradigan, eskirishi joiz ma’lumotni keshla. O‘lchab keshla, hit rate kuzat, keshga majburiy tayanma.

Bu mavzuning Deep/Senior qatlami kerak bo‘lsa (Redis ichki tuzilishi, stampede’ga lock/probabilistic early expiration, multi-level cache, consistent hashing, cache warming) — ayting, qo‘shaman.

Yoki keyingi mavzu: “5-mavzu: Load Balancing” — masshtablashning ikkinchi quroli.