Redis — ioredis, cache, TTL
Muammodan boshlaylik
Sizning API ingizda eng ko'p so'raladigan endpoint — GET /products. Har so'rovda ma'lumotlar bazasiga boriladi, 500 ms kutiladi, natija qaytariladi.
Sekundiga 1000 so'rov kelsa — ma'lumotlar bazasiga sekundiga 1000 ta so'rov. Ma'lumotlar bazasi zo'riqadi, javob sekinlashadi.
Lekin mahsulotlar ro'yxati har 5 daqiqada bir marta o'zgaradi. Nima uchun har so'rovda ma'lumotlar bazasiga borish kerak? Natijani bir joyda saqlab, keyingi so'rovlarda o'sha joydan olsa bo'lmaydimi?
Bu — caching (keshlash). Redis — eng mashhur cache tizimi.
Redis nima?
Redis — xotirada ishlaydigan ma'lumotlar ombori. Ma'lumotlar diskda emas, RAM da saqlanadi — shuning uchun juda tez. O'qish va yozish — mikrosekundlarda.
Hayotiy o'xshatma: siz kutubxonadan kitob oldingiz. Har safar kutubxonaga borish o'rniga — kitobni stolga qo'yasiz. Kerak bo'lganda stoldan olasiz — tez va qulay. Ish tugagach qaytarasiz. Redis — o'sha stol. Ma'lumotlar bazasi — kutubxona.
Redis faqat cache emas — session saqlash, real-time leaderboard, rate limiting uchun ham ishlatiladi. Lekin eng ko'p ishlatilishi — cache.
npm install ioredis
ioredis — eng yaxshi Node.js Redis kutubxonasi. Rasmiy redis paketidan tezroq va imkoniyatlari ko'proq.
Ulanish
// redis.js
const Redis = require('ioredis');
const redis = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD || undefined,
});
redis.on('connect', () => console.log('Redis ga ulandi'));
redis.on('error', (err) => console.error('Redis xatosi:', err.message));
module.exports = redis;
Asosiy operatsiyalar — set va get
Redis — kalit-qiymat ombori. Xuddi JavaScript ob'ekti kabi: kalit bilan saqlanadi, kalit bilan olinadi. Lekin farqi — Redis alohida jarayon, server qayta ishga tushsa ham ma'lumotlar saqlanadi.
const redis = require('./redis');
// Saqlash:
await redis.set('user:1', JSON.stringify({ id: 1, name: 'Ali' }));
// Olish:
const data = await redis.get('user:1');
const user = JSON.parse(data); // { id: 1, name: 'Ali' }
// O'chirish:
await redis.del('user:1');
Redis faqat string saqlaydi — ob'ektlarni JSON.stringify bilan stringga, o'qiganda JSON.parse bilan ob'ektga aylantirish kerak.
TTL — vaqt cheklash
Cache ning eng muhim xususiyati — ma'lumot eskiradi. Mahsulotlar ro'yxati 5 daqiqadan keyin yangilanishi mumkin — cache ham 5 daqiqadan keyin o'chirilishi kerak.
TTL (Time To Live) — ma'lumot qancha vaqt saqlanishi. TTL o'tgach Redis uni o'zi o'chiradi. Siz o'chirishni o'ylamasangiz ham bo'ladi.
// 5 daqiqa (300 soniya) saqlash:
await redis.set('products', JSON.stringify(products), 'EX', 300);
TTL qo'ymaslik — keng tarqalgan xato. Qo'yilmasa ma'lumot abadiy saqlanadi, ma'lumotlar bazasida o'zgarsa ham cache eski qoladi.
Cache pattern — qanday ishlaydi
Cache ning ishlash mantiqini bir marta tushunib olsangiz — hamma joyda ishlatish oson. "Cache-aside" pattern — eng keng tarqalgan:
So'rov keldi
↓
Cache da bormi?
↓ ha ↓ yo'q
Cache dan qaytarish Ma'lumotlar bazasidan ol
(1-2 ms) ↓
Cache ga saqlash (TTL bilan)
↓
Qaytarish (500 ms)
const redis = require('../redis');
const pool = require('../db');
router.get('/products', async (req, res, next) => {
try {
const CACHE_KEY = 'products:all';
// 1. Cache da bormi?
const cached = await redis.get(CACHE_KEY);
if (cached) {
return res.json(JSON.parse(cached));
}
// 2. Yo'q — ma'lumotlar bazasidan ol
const result = await pool.query('SELECT * FROM products');
const products = result.rows;
// 3. Cache ga saqlash — 5 daqiqa
await redis.set(CACHE_KEY, JSON.stringify(products), 'EX', 300);
res.json(products);
} catch (err) {
next(err);
}
});
Birinchi so'rov — ma'lumotlar bazasiga boradi, ~500 ms. Keyingi so'rovlar — Redis dan, ~1 ms. 500 baravar tez.
Cache invalidation — eskirgan cache ni o'chirish
Mahsulot qo'shildi yoki o'zgartirildi — cache eskirdi. Uni o'chirish kerak, aks holda foydalanuvchilar eski ma'lumot ko'radi.
Bu — cache invalidation. Dasturlashdagi eng qiyin muammolardan biri deyiladi — qachon cache ni o'chirish kerakligini bilish.
router.post('/products', async (req, res, next) => {
try {
const result = await pool.query(
'INSERT INTO products (name, price) VALUES ($1, $2) RETURNING *',
[req.body.name, req.body.price]
);
await redis.del('products:all'); // cache o'chirildi — eskirdi
res.status(201).json(result.rows[0]);
} catch (err) {
next(err);
}
});
Keyingi GET /products so'rovida cache yo'q — ma'lumotlar bazasidan yangi ma'lumot olinadi va cache yangilanadi.
Kalit nomlash qoidasi
Redis da kalit nomlash muhim — loyiha o'sganda tartib bo'lishi uchun. Keng tarqalgan qoida: resurs:id yoki resurs:filter.
'products:all' ← barcha mahsulotlar
'products:1' ← 1-mahsulot
'users:1:orders' ← 1-foydalanuvchining buyurtmalari
'products:category:phones' ← telefon mahsulotlari
Tez-tez uchraydigan xatolar
JSON.parse(null) xatosi:
redis.get kalit yo'q bo'lsa null qaytaradi. JSON.parse(null) esa null qaytaradi — xato emas, lekin null.name kabi ishlatilsa xato bo'ladi. Avval tekshirish kerak:
const cached = await redis.get('key');
if (cached) return res.json(JSON.parse(cached));
// cached null bo'lsa — bu blok o'tkazib yuboriladi
TTL qo'ymaslik:
await redis.set('products', JSON.stringify(data));
// TTL yo'q — abadiy saqlanadi, ma'lumot yangilansa ham eski cache qoladi
Redis ishlamasa server ham ishlamay qolishi:
Redis — qo'shimcha tezlashtiruvchi. U ishlamasa ham asosiy funksionallik ishlashi kerak. Redis xatosini ushlab, ma'lumotlar bazasidan olish kerak:
let products;
try {
const cached = await redis.get('products:all');
if (cached) return res.json(JSON.parse(cached));
} catch {
// Redis ishlamasa — davom etamiz, ma'lumotlar bazasidan olamiz
}
const result = await pool.query('SELECT * FROM products');
products = result.rows;
try {
await redis.set('products:all', JSON.stringify(products), 'EX', 300);
} catch {
// Cache ga yozib bo'lmasa ham — javob yuboramiz
}
res.json(products);
Xulosa
Redis — xotirada ishlaydigan tez cache tizimi. Ma'lumotlar bazasiga har so'rovda borish o'rniga — natija Redis da saqlanadi, keyingi so'rovlarda u yerdan olinadi.
TTL — ma'lumot qancha vaqt saqlanishi, o'tgach Redis o'zi o'chiradi. Cache invalidation — ma'lumot o'zgarganda cache o'chiriladi.
Cache-aside pattern: cache da bor → cache dan qaytarish. Yo'q → ma'lumotlar bazasidan olish → cache ga saqlash → qaytarish.
Keyingi dars
18-dars: Database Transaction — nima, qachon kerak, qanday yoziladi