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)
Redis — kalit-qiymat (key-value) ombori, xotirada ishlaydi 0.1-bob:
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, ishonchliRedis 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)
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):
npm install ioredis # (5.2)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'qishRedis 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)
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:
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)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):
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):
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// 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):
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):
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):
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'chiradiKesh 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'damaxmemory+allkeys-lruo'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:
// 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):
// 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 + 1Hash 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
// 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:
// 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):
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'chadi5.15'da
connect-redisbilan 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):
// 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)
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)
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
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)
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)
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)
// 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)
// 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 keshMisol 5 — Ma'lumot tuzilmalari (hash, set, sorted set — 2.9, 2.10)
// 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 10Misol 6 — Atomik hisoblagich (ko'rishlar — 2.11)
// 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)
// 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)
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)
// 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)
// 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
// 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
// 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
// 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
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
// 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)
- Ulanish: ioredis (.env URL); connect/error event (Misol 1, 2.3).
- Cache-aside: mahsulot/ro'yxat keshlash (hit/miss); TTL (Misol 2, 2.5).
- Invalidation: update/delete'da keshni o'chir (Misol 3, 2.6).
- Kesh middleware: GET so'rovlar uchun universal (Misol 4, 2.5).
- Ma'lumot tuzilmalari: hash (profil), set (online), sorted set (reyting — Misol 5, 2.9, 2.10).
- Atomik: ko'rishlar hisoblagich (INCR — Misol 6, 2.11).
- OTP/vaqtinchalik: TTL bilan (Misol 7, 5.18).
- Fail-open: Redis yiqilsa DB'dan (Misol 9, 2.15).
- (Bonus) Pub/Sub: real-time xabar (Misol 8, 2.13).
- (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!