WisarWisar
Dasturlash kitobi/5-QISM — Nodejs19 daqiqa

5.21-bob: Caching — Redis (chuqur)

5-QISM — Node.js Backend · 21-mavzu


1. Kirish va motivatsiya

Bir necha bobda Redisni eslatdik: session store 5.15-bob, OTP saqlash 5.18-bob, rate limit 5.20-bob, Socket.io adapter 5.13-bob, Telegraf sessiya 5.14-bob. Endi uni to'liq, chuqur o'rganamiz — chunki Redis backend dasturchining eng kuchli vositalaridan biri: tezlik va masshtabning kaliti.

Redis (REmote DIctionary Server) — xotirada (RAM — 0.1) ishlaydigan, juda tez ma'lumotlar ombori. Oddiy DB (MongoDB, PostgreSQL — 6) ma'lumotni diskda 0.2-bob saqlaydi — ishonchli, lekin sekinroq. Redis — xotirada — ming marta tezroq (mikrosekund), lekin xotira cheklangan. Redis DB o'rnini bosmaydi — u yordamchi: tez-tez kerak bo'ladigan ma'lumotni kesh (cache) qiladi, vaqtinchalik ma'lumotni (sessiya, OTP) saqlaydi, real-time vazifalarni (navbat, pub/sub) bajaradi.

Asosiy g'oya — caching (keshlash): bir marta hisoblangan/olingan ma'lumotni Redis'da saqlab, keyingi safar DB'ga bormasdan Redis'dan olish. Masalan, mahsulotlar ro'yxati har so'rovda DB'dan olinsa — sekin va DB'ga yuk. Redis'da keshlasak — birinchi so'rov DB'dan (sekin), keyingilari Redis'dan (tez). Bu — yuqori yukli ilovalarning siri.

O'xshatish: DB — katta ombor (uzoqda, hamma narsa bor, lekin borib kelish vaqt oladi). Redis — stol ustidagi tez qo'l yetadigan javon (yonginangda, eng kerakli narsalar, darrov olasiz). Tez-tez kerak narsani har safar omborga bormay, javondan olasiz (kesh). Javon kichik (xotira) — faqat eng kerakli; eskirsa, javondan olib tashlanadi (TTL). Omborsiz javon yashamaydi (Redis — DB o'rnini bosmaydi, yordamchi).

Nega muhim?

  • Tezlik — xotirada (RAM); DB'dan ming marta tez (mikrosekund).
  • Masshtab — DB yukini kamaytiradi (kesh); ko'p server umumiy holat.
  • Ko'p vazifa — kesh, sessiya, OTP, rate limit, navbat 5.22-bob, pub/sub — hammasi.
  • Stack talabi — sizning stack'ingda bor; real ilovalarda standart.

2. Nazariya — chuqur tushuntirish

2.1. Redis nima (xotira ombori)

Rediskalit-qiymat (key-value) ombori, xotirada ishlaydi 0.1-bob:

text
  Redis = ulkan Map (2.9-JS), lekin alohida server (jarayon), tarmoq orqali ulaniladi
  kalit  qiymat:  "user:7"  "{...}"  "otp:998..."  "384726"

  Xotirada (RAM)  juda tez (mikrosekund)
  Disk DB (6)  sekinroq (millisekund) — lekin ko'p, ishonchli

Redis vs DB: Redis — tez, vaqtinchalik, kichik (RAM). DB (6) — sekinroq, doimiy, katta (disk). Birga ishlaydi: doimiy ma'lumot — DB; tez-kerak/vaqtinchalik — Redis. Redis DB o'rnini bosmaydi.

2.2. Redis nima uchun ishlatiladi (use-case'lar)

text
  Caching        — DB natijasini keshlash (eng asosiy — 2.5)
  Session store  — sessiyalar 5.15-bob
  Vaqtinchalik   — OTP 5.18-bob, tasdiqlash kodlari (TTL bilan — 2.7)
  Rate limiting  — so'rov hisoblagich 5.20-bob
  Navbat (queue) — fon vazifalar (BullMQ — 5.22)
  Pub/Sub        — real-time xabar (Socket.io adapter — 5.13, 2.13)
  Leaderboard    — reyting (sorted set — 2.10)
  Hisoblagich    — ko'rishlar, like (INCR — atomik — 2.11)

2.3. O'rnatish va ulanish (ioredis)

Node.js'da Redis bilan ishlash uchun ioredis (mashhur, kuchli) yoki node-redis (rasmiy):

bash
npm install ioredis         # (5.2)
js
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);   // redis://localhost:6379 (5.8)

await redis.set("kalit", "qiymat");                // yozish
const q = await redis.get("kalit");                // o'qish

Redis serveri kerak: Redis — alohida server (Docker — 10.3: docker run redis, yoki cloud — Redis Cloud/Upstash). Lokal dev'da o'rnatasiz yoki Docker'da. ioredis — bir marta yaratiladi, qayta ishlatiladi (5.8 config kabi).

2.4. Asosiy buyruqlar (string)

js
await redis.set("kalit", "qiymat");                // yozish
await redis.set("kalit", "qiymat", "EX", 60);      // 60 sekund TTL bilan (2.7)
await redis.get("kalit");                           // o'qish ("qiymat" yoki null)
await redis.del("kalit");                           // o'chirish
await redis.exists("kalit");                        // bormi (1/0)
await redis.incr("hisob");                          // +1 (atomik — 2.11)
await redis.expire("kalit", 120);                   // TTL o'rnatish
await redis.ttl("kalit");                           // qancha qoldi (sekund)

2.5. Caching — cache-aside strategiyasi (eng muhim)

Eng keng tarqalgan kesh naqshi — cache-aside (lazy loading): avval Redis'dan qidir; bo'lmasa — DB'dan ol, Redis'ga sakla:

text
  So'rov  Redis'da bormi?
            ├─ HA (cache hit)  Redis'dan qaytar (tez!) 
            └─ YO'Q (cache miss)  DB'dan ol (6)  Redis'ga sakla (TTL)  qaytar

  Keyingi so'rovlar  Redis'dan (DB'ga bormaydi — tez, DB yuki kam)
js
async function mahsulotOl(id) {
  const kesh = await redis.get(`product:${id}`);   // 1. Redis'da bormi?
  if (kesh) return JSON.parse(kesh);                // cache hit (tez!)

  const m = await Product.findById(id);             // 2. cache miss  DB (6)
  await redis.set(`product:${id}`, JSON.stringify(m), "EX", 3600);  // 3. Redis'ga (1 soat)
  return m;
}

Cache hit / miss: hit — Redis'da topildi (tez). miss — yo'q, DB'dan olindi (sekin, lekin keyin keshlanadi). Maqsad — hit rationi oshirish (ko'p so'rov Redis'dan).

Cache-aside — eng keng tarqalgani, lekin boshqa naqshlar ham bor (qaysi joyda kesh "to'ldiriladi" va "yoziladi" — shu bilan farqlanadi):

text
  Cache-aside (lazy)   — ilova o'zi: miss bo'lsa DB'dan olib, keshga yozadi (2.5 — yuqorida)
  Read-through         — kesh qatlami o'zi DB'dan oladi (ilova faqat keshga murojaat) — kutubxona/proxy
  Write-through        — yozishda: DB'ga VA keshga birga yoziladi (kesh doim yangi, lekin yozish sekinroq)
  Write-behind         — yozishda: avval keshga, DB'ga keyinroq (fon — tez, lekin yo'qolish xavfi)

Amalda: Node.js'da odatda cache-aside ishlatiladi (sodda, ilova nazoratda). Write-through — kesh hech qachon eskirmasligi muhim bo'lsa. Write-behind — juda ko'p yozishni tezlashtirish kerak bo'lsa (lekin Redis yiqilsa, DB'ga yozilmagan ma'lumot yo'qoladi — ehtiyot). Boshlash uchun cache-aside 2.5-bob yetarli.

2.6. Cache invalidation (keshni yangilash) — qiyin muammo

Kesh muammosi: ma'lumot DB'da o'zgarsa, Redis'dagi eski qoladi (foydalanuvchi eski ma'lumot ko'radi). Buni hal qilish — cache invalidation (mashhur "kompyuter fanidagi ikki qiyin narsa"dan biri):

text
  Strategiyalar:
  1. TTL — kesh muddat bilan (eski avtomatik o'chadi — 2.7) — sodda, ko'p ishlatiladi
  2. Write-through/invalidate — ma'lumot o'zgarsa, keshni darrov o'chir/yangila
  3. Ikkalasi — TTL (xavfsizlik to'ri) + o'zgarishda o'chirish
js
// Ma'lumot o'zgarganda keshni o'chirish (2.6)
async function mahsulotYangila(id, data) {
  await Product.findByIdAndUpdate(id, data);        // DB (6)
  await redis.del(`product:${id}`);                  // keshni o'chir (eski qolmasin)
  // keyingi so'rov DB'dan yangi oladi va qayta keshlaydi (2.5)
}

Oltin qoida: ma'lumot o'zgarganda (update/delete) — tegishli keshni o'chir (invalidate). TTL — qo'shimcha xavfsizlik (agar o'chirishni unutsangiz, muddatdan keyin yangilanadi). Ikkalasi birga — ishonchli.

2.7. TTL (Time To Live) — muddat

Redis kalitiga muddat beriladi; muddat tugaganda Redis avtomatik o'chiradi (OTP, sessiya, kesh uchun ideal):

js
await redis.set("otp:998...", kod, "EX", 120);     // 120 sekund (5.18)
await redis.set("cache", data, "EX", 3600);        // 1 soat
await redis.expire("kalit", 60);                    // mavjud kalitga TTL
await redis.ttl("kalit");                           // qoldi (-1: TTL yo'q; -2: yo'q)

TTL — Redis'ning kuchli tomoni: qo'lda tozalash kerak emas (DB'da eski yozuvlarni o'chirish kerak edi — 5.18). Vaqtinchalik ma'lumot (OTP, kesh, sessiya) — TTL bilan o'zi tozalanadi.

Cache stampede (thundering herd) — ehtiyot bo'ling. Mashhur kalit TTL'i tugagan paytda, bir vaqtda minglab so'rov keladi: hammasi "cache miss" ko'radi va bir vaqtda DB'ga yuguradi (DB cho'kib qoladi — to'da hujumi):

text
  TTL tugadi  1000 so'rov bir vaqtda  1000 ta DB so'rovi (gala!)  DB sekinlashadi/yiqiladi

  Yechimlar:
  - Lock (mutex)  — birinchi miss qoluvchi DB'dan oladi (SETNX), qolganlari biroz kutadi/eski beradi
  - Erta yangilash — TTL tugashidan oldin fonda qayta keshlash (stale-while-revalidate)
  - TTL'ga "jitter" — bir xil TTL bermay, ozgina tasodifiy farq (kalitlar bir paytda tugamaydi)

Qachon muhim: kam mashhur kalitlarda muammo yo'q. Lekin juda ko'p so'rov keladigan bitta kalit (bosh sahifa, mashhur mahsulot) uchun stampede real xavf. Sodda yechim — TTL'ga kichik tasodifiy qo'shimcha (3600 + Math.random()*60), shunda kalitlar bir soniyada birgalikda tugamaydi.

Eviction policy (xotira to'lganda). Redis — xotirada; maxmemory chegarasiga yetganda, qaysi kalitni o'chirish kerakligini maxmemory-policy belgilaydi. Eng ko'p ishlatiladigani — LRU (Least Recently Used — eng uzoq ishlatilmagan kalit o'chiriladi):

text
  noeviction       — o'chirmaydi, yangi yozishni rad etadi (xato) — default
  allkeys-lru      — eng kam ishlatilgan kalitni o'chiradi (kesh uchun ideal — eng mashhuri)
  allkeys-lfu      — eng kam CHASTOTADA ishlatilganni (LFU — ba'zan yaxshiroq)
  volatile-ttl     — TTL'i bor kalitlar ichidan muddati yaqinini o'chiradi

Kesh uchun: allkeys-lru — Redis'ni "toza kesh" sifatida ishlatganda to'g'ri tanlov (xotira to'lsa, eskini o'zi chiqaradi, ilova to'xtamaydi). noeviction (default) — kesh uchun xavfli (xotira to'lsa yozish xato beradi). Production'da maxmemory + allkeys-lru o'rnating 10.3-bob.

2.8. Ma'lumot tuzilmalari — String

Redis faqat string emas — bir nechta ma'lumot tuzilmasi (data structure) beradi (ioredis). Eng oddiysi — String:

js
// String — oddiy qiymat (matn, son, JSON)
await redis.set("ism", "Ali");
await redis.set("hisob", "0");
await redis.incr("hisob");                          // "1" (atomik son — 2.11)
await redis.incrby("hisob", 5);                     // "6"
await redis.mset("a", "1", "b", "2");              // ko'p yozish
await redis.mget("a", "b");                         // ["1", "2"]

2.9. Ma'lumot tuzilmalari — Hash (obyekt)

Hash — bitta kalit ostida maydon-qiymat juftliklari (obyekt kabi — 2.8-JS):

js
// Hash — obyekt (user:7  { ism, email, yosh })
await redis.hset("user:7", { ism: "Ali", email: "ali@a.uz", yosh: "25" });
await redis.hget("user:7", "ism");                  // "Ali"
await redis.hgetall("user:7");                       // { ism, email, yosh }
await redis.hincrby("user:7", "yosh", 1);           // yosh + 1

Hash qachon: obyekt saqlash (foydalanuvchi profili, sessiya). String'da JSON saqlasa ham bo'ladi, lekin hash — bitta maydonni o'qish/o'zgartirish imkonini beradi (butun obyektni emas).

2.10. Ma'lumot tuzilmalari — List, Set, Sorted Set

js
// LIST — tartibli ro'yxat (navbat, oxirgi N element)
await redis.lpush("xabarlar", "a");                 // boshiga qo'sh
await redis.rpush("xabarlar", "b");                 // oxiriga qo'sh
await redis.lrange("xabarlar", 0, 9);              // oxirgi 10 ta

// SET — takrorlanmas to'plam (2.9-JS: Set)
await redis.sadd("teglar", "js", "node", "js");    // {js, node} (takror yo'q)
await redis.smembers("teglar");                      // ["js", "node"]
await redis.sismember("teglar", "js");             // bor (1)

// SORTED SET — ball bilan tartiblangan (leaderboard, reyting — 2.2)
await redis.zadd("reyting", 100, "Ali", 85, "Vali");
await redis.zrevrange("reyting", 0, 9, "WITHSCORES");  // top 10 (yuqori ball)

Qaysi qachon: List — tartibli (oxirgi xabarlar, navbat); Set — takrorlanmas (teglar, online foydalanuvchilar — 5.13); Sorted Set — ballga qarab tartibli (reyting, leaderboard, vaqt bo'yicha). Bu tuzilmalar — Redis'ning kuchi (oddiy kesh emas).

2.11. Atomik amallar (race condition'siz)

Redis amallari atomik (bo'linmas — bir vaqtda ikki so'rov buzilmaydi). INCR — atomik hisoblagich:

js
// Atomik hisoblagich (race condition yo'q — 5.1: event loop)
await redis.incr(`views:post:${id}`);              // ko'rishlar (bir vaqtda ko'p so'rov — to'g'ri)

//  Bu race condition'li bo'lardi (oddiy DB'da):
//    val = get(); val = val + 1; set(val);   ikki so'rov orasida yo'qolishi mumkin
//  INCR — atomik (Redis bir vaqtda bittasini bajaradi)

Atomiklik — Redis'ning muhim xususiyati: INCR, SETNX (faqat yo'q bo'lsa yoz — lock uchun) kabi amallar race condition'siz 5.1-bob. Hisoblagich, rate limit 5.20-bob, distributed lock uchun.

2.12. Session store (sessiya — 5.15)

Redis — sessiya saqlash uchun ideal (TTL + tez + ko'p server umumiy):

text
  session:abc123  { userId: 7, ... }  (TTL: 24 soat)

   ko'p server bir Redis'ga ulanadi  sessiya barcha serverda bir xil (5.15: 2.9)
   TTL bilan eski sessiyalar avtomatik o'chadi

5.15'da connect-redis bilan ko'rdik. Redis — session store sifatida standart (xotira store ko'p serverda ishlamaydi).

2.13. Pub/Sub (nashr/obuna — real-time)

Redis Pub/Sub — bir joy xabar e'lon qiladi (publish), boshqalar obuna bo'lib (subscribe) oladi. Bu — Socket.io'ni ko'p serverga masshtablashda ishlatiladi (5.13: 2.16):

js
// Nashr qiluvchi
await redis.publish("yangiliklar", JSON.stringify({ matn: "Salom" }));

// Obunachi (alohida ulanish)
const sub = new Redis(process.env.REDIS_URL);
sub.subscribe("yangiliklar");
sub.on("message", (channel, message) => {
  console.log("Yangi xabar:", JSON.parse(message));   // barcha obunachilar oladi
});

Pub/Sub vs Queue 5.22-bob: Pub/Sub — "bir martalik e'lon" (obuna bo'lmagan — o'tkazib yuboradi; saqlanmaydi). Queue (BullMQ — 5.22) — "ishonchli navbat" (saqlanadi, qayta urinish). Real-time tarqatish — Pub/Sub; fon vazifa — Queue.

2.14. Caching — nimani keshlash kerak (va kerak emas)

text
   Keshlash yaxshi:
  - Tez-tez o'qiladigan, kam o'zgaradigan (mahsulot ro'yxati, sozlamalar)
  - Og'ir hisoblash natijasi (statistika, agregatsiya — 6)
  - Tashqi API javobi (sekin/pulli)

   Keshlash yomon/ehtiyot:
  - Tez-tez o'zgaradigan (real-time narx) — eski ko'rsatadi
  - Har foydalanuvchiga xos maxfiy (ehtiyot — 14)
  - Bir marta o'qiladigan (kesh foydasi yo'q)

2.15. Xavfsizlik va best practices (14)

text
   Redis'ga parol/auth (production — 14); tashqariga ochiq qoldirma
   Kalit nomi konvensiyasi (user:7, product:42, otp:998... — 2.4)
   TTL ber (xotira to'lib qolmasin — 2.7); maxmemory policy (eskini o'chirish)
   Maxfiy ma'lumotni ehtiyot (kerak bo'lsa shifrla — 14)
   Katta qiymatdan saqlan (Redis — kichik, tez ma'lumot uchun)
   Redis URL .env'da 5.8-bob; ulanish xatosini boshqar 5.10-bob
   Kesh "fail-open" — Redis yiqilsa, DB'dan ishla (ilova to'xtamasin)

3. Sintaksis — tez ma'lumotnoma

js
import Redis from "ioredis";
const redis = new Redis(process.env.REDIS_URL);    // (5.8)

// String + TTL (2.4, 2.7)
await redis.set("k", "v", "EX", 60);   await redis.get("k");   await redis.del("k");
await redis.incr("hisob");                          // atomik (2.11)

// Hash 2.9-bob: hset/hget/hgetall
// List 2.10-bob: lpush/rpush/lrange
// Set 2.10-bob: sadd/smembers
// Sorted Set 2.10-bob: zadd/zrevrange

// Cache-aside (2.5)
const k = await redis.get(key); if (k) return JSON.parse(k);
const d = await DB...; await redis.set(key, JSON.stringify(d), "EX", 3600);

// Pub/Sub 2.13-bob: publish / subscribe + on("message")

4. Batafsil kod namunalari

Misol 1 — Ulanish va asosiy amallar (2.3, 2.4)

js
import Redis from "ioredis";
import { config } from "./config/index.js";         // (5.8)

export const redis = new Redis(config.redis.url);    // bir marta

redis.on("connect", () => console.log(" Redis ulandi"));   // (5.12)
redis.on("error", (err) => console.error("Redis xato:", err));   // (5.10)

// Asosiy amallar (2.4)
await redis.set("test", "salom", "EX", 60);          // TTL bilan
const q = await redis.get("test");                    // "salom"
await redis.incr("tashriflar");                       // atomik (2.11)

Misol 2 — Cache-aside (mahsulot keshi — 2.5)

js
const KESH_MUDDAT = 3600;                            // 1 soat

export async function mahsulotOl(id) {
  const kalit = `product:${id}`;

  // 1. Redis'dan qidir (2.5)
  const kesh = await redis.get(kalit);
  if (kesh) {
    console.log("Cache HIT");                         // tez!
    return JSON.parse(kesh);
  }

  // 2. Cache miss  DB'dan (6)
  console.log("Cache MISS");
  const mahsulot = await Product.findById(id);
  if (!mahsulot) return null;

  // 3. Redis'ga sakla (keyingi safar uchun — 2.5)
  await redis.set(kalit, JSON.stringify(mahsulot), "EX", KESH_MUDDAT);
  return mahsulot;
}

Misol 3 — Cache invalidation (yangilashda — 2.6)

js
// O'zgartirishda keshni o'chirish (eski qolmasin — 2.6)
export async function mahsulotYangila(id, data) {
  const mahsulot = await Product.findByIdAndUpdate(id, data, { new: true });   // DB (6)
  await redis.del(`product:${id}`);                  // keshni o'chir (2.6)
  // Ro'yxat keshini ham (agar bor bo'lsa)
  await redis.del("products:all");
  return mahsulot;
}

export async function mahsulotOchir(id) {
  await Product.findByIdAndDelete(id);
  await redis.del(`product:${id}`);                  // kesh ham (2.6)
  await redis.del("products:all");
}

Misol 4 — Kesh middleware (qayta ishlatiladigan — 2.5)

js
// Universal kesh middleware (GET so'rovlar uchun — 5.6, 2.5)
export const cache = (muddat = 300) => async (req, res, next) => {
  const kalit = `cache:${req.originalUrl}`;          // URL bo'yicha kalit

  const kesh = await redis.get(kalit);
  if (kesh) {
    return res.json(JSON.parse(kesh));                // cache hit (tez!)
  }

  // res.json'ni "ushlab", keshlash uchun o'rab olamiz
  const aslJson = res.json.bind(res);
  res.json = (data) => {
    redis.set(kalit, JSON.stringify(data), "EX", muddat);   // keshla (2.7)
    return aslJson(data);
  };
  next();
};

// Ishlatish (5.6)
app.get("/api/products", cache(600), getProducts);   // 10 daqiqa kesh

Misol 5 — Ma'lumot tuzilmalari (hash, set, sorted set — 2.9, 2.10)

js
// HASH — foydalanuvchi profili (2.9)
await redis.hset(`user:${id}`, { ism: "Ali", email: "ali@a.uz" });
const profil = await redis.hgetall(`user:${id}`);    // { ism, email }

// SET — online foydalanuvchilar (5.13, 2.10)
await redis.sadd("online", userId);                  // qo'sh (takrorlanmas)
await redis.srem("online", userId);                  // o'chir (disconnect)
const online = await redis.smembers("online");        // ro'yxat
const soni = await redis.scard("online");            // soni

// SORTED SET — reyting (leaderboard — 2.10)
await redis.zincrby("reyting", 10, userId);          // ball +10
const top10 = await redis.zrevrange("reyting", 0, 9, "WITHSCORES");  // top 10

Misol 6 — Atomik hisoblagich (ko'rishlar — 2.11)

js
// Post ko'rishlari (atomik — race condition yo'q — 2.11)
export async function korishQoshil(postId) {
  return redis.incr(`views:post:${postId}`);         // atomik +1
}

// Rate limiting (oddiy — 5.20 ruhida, 2.11)
export async function rateLimit(ip, limit = 100, window = 900) {
  const kalit = `rate:${ip}`;
  const soni = await redis.incr(kalit);
  if (soni === 1) await redis.expire(kalit, window);  // birinchi so'rovda TTL
  return soni <= limit;                               // chegaradan oshmaganmi
}

Misol 7 — OTP saqlash (5.18, TTL — 2.7)

js
// OTP — Redis TTL bilan (5.18, 2.7)
export async function otpSaqla(telefon, kodHash) {
  await redis.set(`otp:${telefon}`, kodHash, "EX", 120);   // 2 daqiqa (avtomatik o'chadi)
  await redis.set(`otp:attempts:${telefon}`, "0", "EX", 120);
}
export async function otpOl(telefon) {
  return redis.get(`otp:${telefon}`);                 // null bo'lsa — muddat tugagan
}
export async function otpOchir(telefon) {
  await redis.del(`otp:${telefon}`, `otp:attempts:${telefon}`);   // ishlatilgach (5.18)
}

Misol 8 — Pub/Sub (2.13)

js
import Redis from "ioredis";

// Nashr qiluvchi (asosiy ulanish)
export async function elonQil(kanal, data) {
  await redis.publish(kanal, JSON.stringify(data));   // (2.13)
}

// Obunachi (ALOHIDA ulanish kerak — subscribe rejimida boshqa buyruq ishlamaydi)
const subscriber = new Redis(config.redis.url);
subscriber.subscribe("buyurtmalar", "bildirishnomalar");
subscriber.on("message", (kanal, xabar) => {
  const data = JSON.parse(xabar);
  if (kanal === "buyurtmalar") console.log("Yangi buyurtma:", data);
  // Socket.io orqali clientlarga (5.13)
});

Misol 9 — Fail-open (Redis yiqilsa, DB'dan — 2.15)

js
// Redis ishlamasa ham, ilova to'xtamasin (DB'dan ishla — 2.15)
export async function xavfsizKeshOl(id) {
  try {
    const kesh = await redis.get(`product:${id}`);
    if (kesh) return JSON.parse(kesh);
  } catch (err) {
    console.error("Redis o'qishda xato (DB'dan davom):", err.message);   // (5.12)
    // Redis yiqildi — to'xtamaymiz, DB'dan olamiz
  }
  const mahsulot = await Product.findById(id);         // DB (6) — har doim ishlaydi
  redis.set(`product:${id}`, JSON.stringify(mahsulot), "EX", 3600)
    .catch(() => {});                                  // keshlash xato bersa, e'tiborsiz
  return mahsulot;
}

Misol 10 — Pipelining (ko'p amalni birga — performance)

js
// Ko'p amalni bitta so'rovda (tarmoq tejaydi — performance)
const pipeline = redis.pipeline();
pipeline.set("a", "1");
pipeline.incr("hisob");
pipeline.expire("a", 60);
pipeline.hgetall("user:7");
const natijalar = await pipeline.exec();              // hammasi birga bajariladi
//  4 ta alohida so'rov o'rniga 1 ta (tarmoq sayohati kam — tezroq)

5. To'g'ri va noto'g'ri holatlar

1) Keshni invalidatsiya qilmaslik

js
//  ma'lumot o'zgardi, kesh eski (foydalanuvchi eski ko'radi — 2.6)
await Product.findByIdAndUpdate(id, data);

//  keshni o'chir
await Product.findByIdAndUpdate(id, data);
await redis.del(`product:${id}`);

2) TTL'siz kesh

js
//  kesh abadiy (xotira to'ladi, eskirib qoladi — 2.7)
await redis.set(`product:${id}`, data);

//  TTL
await redis.set(`product:${id}`, data, "EX", 3600);

3) Redis yiqilsa ilova to'xtaydi

js
//  Redis xatosi butun so'rovni buzadi (2.15)
const kesh = await redis.get(key);   // Redis o'lik  500

//  fail-open (try/catch, DB'dan davom — Misol 9)

4) Hamma narsani keshlash

text
 tez-tez o'zgaradigan/maxfiy ma'lumotni ham keshlash (2.14)
 tanlab: tez-o'qiladigan, kam-o'zgaradigan (2.14)

5) JSON'ni parse/stringify unutish

js
//  obyektni to'g'ridan (Redis faqat string)
await redis.set("user", { ism: "Ali" });   // "[object Object]"!

//  JSON.stringify / parse
await redis.set("user", JSON.stringify({ ism: "Ali" }));
JSON.parse(await redis.get("user"));

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — ECONNREFUSED (Redis ulanmaydi)

Sababi: Redis serveri ishlamayapti, yoki URL noto'g'ri 2.3-bob. Yechimi: Redis'ni ishga tushiring (Docker/lokal — 10.3); REDIS_URL tekshiring 5.8-bob.

Xato 2 — Eski ma'lumot ko'rsatiladi

Sababi: kesh invalidatsiya qilinmagan 2.6-bob. Yechimi: o'zgarishda del; TTL qo'shing (Misol 3).

Xato 3 — [object Object] saqlangan

Sababi: obyekt JSON'siz saqlandi (5-xato). Yechimi: JSON.stringify/parse.

Xato 4 — Xotira to'lib ketdi

Sababi: TTL'siz ko'p kalit 2.7-bob. Yechimi: TTL bering; maxmemory-policy (eskini o'chirish — allkeys-lru).

Xato 5 — Subscribe rejimida boshqa buyruq ishlamaydi

Sababi: subscribe ulanishida get/set mumkin emas 2.13-bob. Yechimi: Pub/Sub uchun alohida ulanish (Misol 8).

Xato 6 — Kesh hit ratio past (foyda kam)

Sababi: TTL juda qisqa, yoki noto'g'ri ma'lumot keshlangan 2.14-bob. Yechimi: TTL moslang; tez-o'qiladigan ma'lumotni keshlang.


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • DB (6): kesh DB yukini kamaytiradi (cache-aside).
  • Session 5.15-bob: Redis session store.
  • OTP 5.18-bob: TTL bilan kod saqlash.
  • Rate limiting 5.20-bob: so'rov hisoblagich; Redis store.
  • Socket.io 5.13-bob: Redis adapter (Pub/Sub — masshtab).
  • Telegram 5.14-bob: sessiya.
  • Navbat 5.22-bob: BullMQ — Redis ustida.
  • Env 5.8-bob: REDIS_URL.
  • Error handling 5.10-bob: fail-open.
  • DevOps 10.3-bob: Redis Docker/cloud.
  • NestJS 8.19-bob: cache-manager, @nestjs/cache — shu g'oya.

8. Eng yaxshi amaliyotlar (best practices)

  • Cache-aside (Redis DB keshla — 2.5); tez-o'qiladigan, kam-o'zgaradigan 2.14-bob.
  • Cache invalidation — o'zgarishda del + TTL (ikkalasi — 2.6).
  • TTL doim ber (xotira to'lmasin; vaqtinchalik o'zi o'chadi — 2.7).
  • To'g'ri tuzilma (hash — obyekt, set — takrorlanmas, sorted set — reyting — 2.9, 2.10).
  • Atomik amallar (INCR — race condition'siz — 2.11).
  • JSON.stringify/parse (obyekt uchun — 5-xato).
  • Fail-open (Redis yiqilsa, DB'dan — ilova to'xtamasin — 2.15, Misol 9).
  • Kalit konvensiyasi (user:7, product:42 — 2.4).
  • Pub/Sub uchun alohida ulanish 2.13-bob; pipelining (ko'p amal — Misol 10).
  • Production: parol/auth, maxmemory policy, .env URL (2.15, 14).

9. Amaliy loyiha: "Redis Keshlash va Tezlashtirish Tizimi"

Redis'ni professional darajada mustahkamlash.

Maqsad

Redis bilan caching, ma'lumot tuzilmalari va real-time imkoniyatlarni birlashtirib, tez, masshtablanadigan backend qurish.

Talablar (requirements)

  1. Ulanish: ioredis (.env URL); connect/error event (Misol 1, 2.3).
  2. Cache-aside: mahsulot/ro'yxat keshlash (hit/miss); TTL (Misol 2, 2.5).
  3. Invalidation: update/delete'da keshni o'chir (Misol 3, 2.6).
  4. Kesh middleware: GET so'rovlar uchun universal (Misol 4, 2.5).
  5. Ma'lumot tuzilmalari: hash (profil), set (online), sorted set (reyting — Misol 5, 2.9, 2.10).
  6. Atomik: ko'rishlar hisoblagich (INCR — Misol 6, 2.11).
  7. OTP/vaqtinchalik: TTL bilan (Misol 7, 5.18).
  8. Fail-open: Redis yiqilsa DB'dan (Misol 9, 2.15).
  9. (Bonus) Pub/Sub: real-time xabar (Misol 8, 2.13).
  10. (Bonus) Pipelining: ko'p amal birga (Misol 10).

Maslahatlar (hint)

  • Cache-aside: get bo'lmasa DB set 2.5-bob.
  • Invalidation: o'zgarishda del (2.6, 1-xato).
  • TTL: set(k, v, "EX", sek) (2.7, 2-xato).
  • JSON.stringify/parse (5-xato).
  • Tuzilma: hash (hgetall), set (smembers), sorted set (zrevrange) — 2.10.
  • Fail-open: try/catch, DB'dan davom 2.15-bob.
  • Pub/Sub: alohida ulanish (5-xato).

"Tayyor" mezonlari (acceptance criteria)

  • Redis ulanadi (event handling).
  • Cache-aside ishlaydi (hit/miss; 2-so'rov tez).
  • Invalidation: o'zgarishda kesh yangilanadi.
  • Kesh middleware GET'larni keshlaydi.
  • Hash/set/sorted set ishlatilgan.
  • Atomik hisoblagich (INCR).
  • OTP/vaqtinchalik TTL bilan.
  • Fail-open (Redis yiqilsa, DB'dan).
  • (Bonus) Pub/Sub / pipelining.

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda backend tezligi va masshtabining kalitini — Redis — chuqur o'rgandik:

  • Redis — xotira (RAM) ombori; DB'dan ming marta tez; yordamchi (DB o'rnini bosmaydi — 2.1).
  • Use-case'lar — kesh, sessiya, OTP, rate limit, navbat, pub/sub 2.2-bob; ioredis 2.3-bob.
  • Cache-aside (Redis DB keshla — 2.5); invalidation (o'zgarishda o'chir — 2.6); TTL (avtomatik o'chish — 2.7).
  • Ma'lumot tuzilmalari — String, Hash (obyekt), List/Set/Sorted Set (2.8-2.10); atomik (INCR — 2.11).
  • Session 5.15-bob, Pub/Sub (real-time — 2.13); fail-open (Redis yiqilsa DB — 2.15); xavfsizlik (TTL, auth, .env — 14).

Keyingi bob — 5.22-bob: Navbatlar (Queues) — BullMQ, fon vazifalari, cron. Redis'ni bildik; endi uning ustida quriladigan muhim mexanizmni — navbat (queue) — o'rganamiz. Og'ir vazifalarni (email yuborish — 5.19, rasm qayta ishlash, hisobot) fonda, so'rovni bloklamasdan bajarish (BullMQ); va cron bilan rejalashtirilgan vazifalar (har kuni hisobot, tozalash).


Foydalanilgan rasmiy/ishonchli manbalar

  • ioredis.org — ioredis (buyruqlar, data structures, pub/sub, pipelining)
  • redis.io — Redis data types (string, hash, list, set, sorted set), TTL
  • Better Stack — Node.js caching with Redis (cache-aside, invalidation)

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
5.21-bob: Caching — Redis (chuqur) — Wisar