Redis faqat Cache emas - 6 ta muammo, bitta yechim
Suhbatlarda "Redis nima?" deb so'rashsa, 90% holatda bitta javobni eshitamiz:
"Cache qilish uchun, aka."
Bu to'g'ri. Lekin Redis'ni faqat cache sifatida ishlatish oxirgi iPhone sotib olib, undan faqat kalkulyator sifatida foydalanishdek gap.
Bugun Redis'ning haqiqiy qudratini ko'ramiz. Backend arxitekturasida u bitta o'zi qanday 6 ta jiddiy muammoni yechadi.
Redis nima keshdan tashqari
In-Memory Data Structure Server xotirada (RAM) ishlaydigan ma'lumotlar strukturalari serveri.
Nega u bunday tez?
3 ta asosiy sabab:
- RAM asosida disk o'qishdan 1000x tezroq
- Single-threaded lock va context switch muammolari yo'q, qator ketma-ket bajariladi
- I/O Multiplexing bitta thread minglab ulanishni boshqaradi
Eng muhimi Redis ichida faqat oddiy String key -> value emas, quyidagi tayyor ma'lumotlar strukturalari bor:
| Struktura | Ichki ishlashi | Asosiy buyruqlar |
|---|---|---|
| String | SDS (Simple Dynamic String) | SET, GET, INCR |
| List | Quicklist (linked list + ziplist) | LPUSH, RPOP, BRPOP |
| Hash | Listpack yoki Hash Table | HSET, HGET, HGETALL |
| Set | Hash Table | SADD, SMEMBERS, SINTER |
| Sorted Set | Skip List + Hash Table | ZADD, ZRANGE, ZREVRANK |
Aynan mana shu strukturalar unga cache'dan tashqari yana 5 ta super qudrat beradi.
Message Queue navbat tizimi
Foydalanuvchi "Hisobot tayyorla" tugmasini bosdi. Hisobot 20 sekund vaqt oladi. HTTP request'ni 20 sekund ushlab turish bu daxshat. Foydalanuvchi brauzerini yopib ketadi.
Bunga yechim vazifani navbatga tashlab, orqa fonda (background worker) bajarish.
Ko'pchilik buning uchun RabbitMQ o'rnatadi. Lekin tizim katta bo'lmasa, Redis buni muammosiz eplaydi.
Qanday ishlaydi? Redis'ning List strukturasi xuddi navbat (FIFO queue) kabi ishlaydi:
Producer --[LPUSH]--> [task3, task2, task1] --[RPOP/BRPOP]--> Consumer
LPUSHchap (bosh) tomonga element qo'shadiRPOPo'ng (dum) tomongidan oladiBRPOPblocking POP: navbat bo'sh bo'lsa, yangi element kelguncha kutadi (polling yo'q!)
Java misoli (Spring Boot + Jedis):
// Producer vazifani navbatga qo'shish
@Service
public class ReportProducer {
private final JedisPool jedisPool;
public void scheduleReport(String reportData) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.lpush("report:queue", reportData);
log.info("Vazifa navbatga qo'shildi");
}
}
}
// Consumer - orqa fonda ishlaydi
@Component
public class ReportConsumer {
@Scheduled(fixedDelay = 100) // yoki alohida thread
public void processQueue() {
try (Jedis jedis = jedisPool.getResource()) {
// 30 soniya kutadi, yangi element kelguncha
List<String> result = jedis.brpop(30, "report:queue");
if (result != null) {
String task = result.get(1);
generateReport(task); // og'ir ish bu yerda
}
}
}
}
Qachon Redis Queue yetarli emas?
Agar xabarlar saqlanib qolishi shart bo'lsa (worker o'chib qolsa ham yo'qolmasin) Redis Streams yoki to'laqonli RabbitMQ/Kafka kerak. Redis List'da BRPOP qilgan element qaytarib bo'lmaydi agar worker birinchi marta olib, ishlatmay o'chib qolsa, u yo'qoladi.
Distributed Lock
Omborda oxirgi 1 ta mahsulot bor. 3 ta foydalanuvchi bir vaqtda "Sotib ol" tugmasini bosdi. Uchala server ham bazadan quantity = 1 o'qidi. Hammasi ham sotishga ruxsat berdi.
Java'dagi synchronized bloki yordamimi? Yo'q! U faqat bitta JVM ichida ishlaydi. 3 ta alohida server bo'lsa, u ishlamaydi.
Yechim: Redis'da global qulf barcha serverlar ko'radigan yagona egalik belgisi.
Mexanizm — SET NX EX:
SET lock:product_99 <uuid> NX EX 10
NXfaqat kalit yo'q bo'lsa qo'y (Set if Not eXists)EX 1010 soniyadan keyin avtomatik o'chir (dead lock oldini oladi)<uuid>kimning qulflagan, boshqa server uchun emas!
Java misoli (Redisson tavsiya etilgan kutubxona):
@Service
public class ProductService {
private final RedissonClient redisson;
public boolean purchaseLastProduct(Long productId) {
// Redisson lock UUID + TTL avtomatik
RLock lock = redisson.getLock("lock:product:" + productId);
try {
// 3 soniya kutib ko'r, 10 soniyada avtomatik o'chir
boolean acquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!acquired) {
throw new RuntimeException("Mahsulot boshqasi tomonidan band!");
}
// bazadan o'q, tekshir, yoz
Product product = productRepository.findById(productId);
if (product.getQuantity() < 1) {
throw new RuntimeException("Mahsulot tugagan!");
}
product.setQuantity(product.getQuantity() - 1);
productRepository.save(product);
return true;
} finally {
// faqat o'z lockini o'chir!
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
Nega UUID muhim? Agar A serverning lock muddati tugasa va B server uni olsa, keyin A o'z ishini bitirib lockni o'chirmoqchi bo'lsa B'ning lockini o'chirib yuboradi! UUID bilan bu muammo yo'q u o'z nishonimgina o'chiraman.
O'tgan safargi "Username band" maqolasida ham aynan shu mavzuni chuqurroq ko'rib chiqqandik.
Rate Limiter
Bitta foydalanuvchi 1 minutda 10 000 ta so'rov yubordi. Tizim ishdan chiqdi.
Buni "1 minutda ko'pi bilan 60 ta so'rov" qoidasi Redis INCR + EXPIRE bilan hal qilsak bo'ladi.
Mexanizm:
1. Request keldi: user_id = 42
2. Redis'da kalit: "rate:user:42" ni INCR qil
3. Agar birinchi so'rov bo'lsa (INCR natijasi = 1): EXPIRE 60 qo'y
4. Agar qiymat > 60: HTTP 429 Too Many Requests qaytardir
Java misoli (Spring + Redis):
@Component
public class RateLimiterService {
private final StringRedisTemplate redis;
private static final int MAX_REQUESTS = 60;
private static final int WINDOW_SECONDS = 60;
public boolean isAllowed(String userId) {
String key = "rate:" + userId;
// Atomik operatsiya increment + get
Long count = redis.opsForValue().increment(key);
if (count == 1) {
// Birinchi so'rov 60 soniyalik oyna boshlaydi
redis.expire(key, Duration.ofSeconds(WINDOW_SECONDS));
}
return count <= MAX_REQUESTS;
}
}
// Spring MVC interceptor'da ishlatish
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String userId = getUserId(request); // JWT'dan olinadi
if (!rateLimiter.isAllowed(userId)) {
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("{\"error\": \"Rate limit exceeded\"}");
return false;
}
return true;
}
}
Ilg'or usul Sliding Window:
Yuqoridagi Fixed Window'da muammo bor: 0:59 da 60 ta, 1:00 da yana 60 ta = 2 daqiqada 120 ta so'rov. Buning uchun Redis Sorted Set ishlatiladi:
public boolean isAllowedSlidingWindow(String userId) {
long now = System.currentTimeMillis();
long windowStart = now - 60_000; // 60 soniya oldin
String key = "sliding:" + userId;
ZSetOperations<String, String> zset = redis.opsForZSet();
// Eski so'rovlarni o'chir
zset.removeRangeByScore(key, 0, windowStart);
// Joriy so'rov sonini ol
Long count = zset.zCard(key);
if (count != null && count >= MAX_REQUESTS) {
return false;
}
// Yangi so'rovni qo'sh
zset.add(key, UUID.randomUUID().toString(), now);
redis.expire(key, Duration.ofSeconds(70)); // oynadan birozroq uzun
return true;
}
Real-time Xabarlar
Chat ilovasi qurmoqchisiz. Bitta foydalanuvchi xabar yozdi boshqalarga real-time yetib borishi kerak.
Redis'da Pub/Sub xuddi radio-stansiya kabi.
[Abdukarim xabar yozadi] --PUBLISH--> Redis Channel: "chat:room:101"
|
SUBSCRIBE qilganlar: [Ali], [Vali], [Hamid]
hammasi bir vaqtda oladi
Java misoli (Spring Data Redis):
// Publisher xabar yuboradi
@Service
public class ChatPublisher {
private final StringRedisTemplate redis;
public void sendMessage(String roomId, String message) {
redis.convertAndSend("chat:room:" + roomId, message);
}
}
// Subscriber xabarlarni qabul qiladi
@Component
public class ChatSubscriber implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
String chatMessage = new String(message.getBody());
// WebSocket orqali frontendga yuborish
webSocketSession.sendMessage(new TextMessage(chatMessage));
}
}
// Configuration Spring Boot'da
@Configuration
public class RedisConfig {
@Bean
public RedisMessageListenerContainer container(
RedisConnectionFactory factory, ChatSubscriber subscriber) {
RedisMessageListenerContainer container =
new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
container.addMessageListener(subscriber,
new PatternTopic("chat:room:*")); // barcha xonalar
return container;
}
}
Muhim ogohlantirish:
Redis Pub/Sub "Fire and Forget" tamoyilida ishlaydi. Agar subscriber o'chiq bo'lsa xabar yo'qoladi, saqlanib qolmaydi.
- Slack, Discord Redis Pub/Sub ishlatadilar (lekin yuqori yuklamada Kafka bilan birga)
- Agar xabarlar saqlanib qolishi shart bo'lsa Redis Streams yoki Kafka
Session Management - Stateless Arxitektura
Foydalanuvchi Server A ga login qildi. Keyingi request Server B ga tushdi B foydalanuvchini tanimaydi.

Buning yechimi barcha serverlar foydalanadigan yagona markazlashgan session ombori.
Oqim:
1. Foydalanuvchi login qiladi -> User Service
2. Session yaratiladi Redis'da: "session:abc123" = {user_id, roles, email}
3. "abc123" Cookie orqali brauzerga qaytariladi
4. Keyingi request: Cookie -> Har qaysi server Redis'dan session'ni tekshiradi
Spring Session + Redis (faqat 2 ta annotation!):
// pom.xml
// spring-session-data-redis dependency qo'shish
// Application.java
@EnableRedisHttpSession
@SpringBootApplication
public class Application { ... }
// application.yml
spring:
session:
store-type: redis
timeout: 1800s # 30 daqiqa
redis:
host: localhost
port: 6379
Spring Session avtomatik ravishda HttpSession'ni Redis'ga yo'naltiradi. Hech qanday qo'shimcha kod shart emas
Nima saqlanadi Redis'da?
"spring:session:sessions:abc123" = {
"sessionAttr:user_id": 27,
"sessionAttr:email": "user@example.com",
"sessionAttr:roles": ["ADMIN"],
"lastAccessedTime": 1735555200000,
"maxInactiveInterval": 1800
}
Leaderboard (Reyting tizimi)
Bu ko'pchilik bilmaydigan lekin juda kuchli qo'llanilish. Instagram'da "eng ko'p like olgan postlar" yoki o'yin ilovasida "top 10 o'yinchilar" ko'rsatish uchun Redis Sorted Set (ZSet) har element uchun score bor va u bo'yicha tartiblangan.
@Service
public class LeaderboardService {
private final StringRedisTemplate redis;
private static final String LEADERBOARD_KEY = "game:leaderboard";
// Ball qo'shish yoki yangilash O(log n)
public void updateScore(String playerId, double points) {
redis.opsForZSet().incrementScore(LEADERBOARD_KEY, playerId, points);
}
// Top 10'ni olish O(log n + k)
public List<PlayerScore> getTopPlayers(int count) {
Set<ZSetOperations.TypedTuple<String>> topPlayers =
redis.opsForZSet()
.reverseRangeWithScores(LEADERBOARD_KEY, 0, count - 1);
return topPlayers.stream()
.map(entry -> new PlayerScore(
entry.getValue(), // playerId
entry.getScore() // ball
))
.collect(Collectors.toList());
}
// Foydalanuvchining o'rnini bilish
public Long getPlayerRank(String playerId) {
// null bo'lmasa, o'rni 0-based qaytaradi
Long rank = redis.opsForZSet()
.reverseRank(LEADERBOARD_KEY, playerId);
return rank != null ? rank + 1 : null; // 1-based qilish
}
}
Bir millionlab o'yinchi bo'lsa ha ZREVRANGE O(log n + k) da ishlaydi PostgrSQL'da ORDER BY score DESC LIMIT 10 uchun full scan kerak bo'lishi mumkin.
Redis qanday mos keladi?

Redis 2010 yildan beri oddiy keshdan boshlab to'laqonli klaster arxitekturasigacha o'sdi. Hozir Airbnb, Uber, Slack, Twitter kabi gigantlar ishlatadi va ularning har biri Redis'ni turli maqsadlarda ishlatadi.
Cheat Sheet Qaysi muammo uchun qaysi yechim?
| Muammo | Redis Strukturasi | Asosiy buyruqlar | Muqobil |
|---|---|---|---|
| Cache | String, Hash | SET nx ex, HSET |
Memcached |
| Message Queue | List / Streams | LPUSH, BRPOP, XADD |
RabbitMQ, Kafka |
| Distributed Lock | String (atomic) | SET key uuid NX EX |
Zookeeper |
| Pub/Sub | Built-in channel | PUBLISH, SUBSCRIBE |
Kafka, NATS |
| Rate Limiting | String / Sorted Set | INCR, EXPIRE, ZADD |
Nginx + Lua |
| Session | Hash | HSET, EXPIRE |
DB sessions |
| Leaderboard | Sorted Set | ZADD, ZREVRANGE |
SQL ORDER BY |
| Counter | String | INCR, INCRBY |
DB atomic update |
Redis nima emas?
Redis faqat kesh emas. U ma'lumotlar strukturalari serveri:
- Navbat kerakmi? -> Redis List
- Global qulf kerakmi? -> Redis SETNX
- Real-time xabarmi? -> Redis Pub/Sub
- So'rovlarni cheklashmi? -> Redis INCR
- Session boshqaruvimi? -> Redis + Spring Session
- Reyting kerakmi? -> Redis Sorted Set
Keyingi safar intervyuda "Redis nima?" deb so'rashsa, endi javob tayyor "Redis In-Memory Data Structure Server bo'lib, u faqat kesh emas, balki distributed lock, message queue, rate limiter, pub/sub va session management muammolarini ham bitta instrument bilan yechadi."
Suhbat oluvchi ko'zlarida chaqmoq chaqnaydi ishoning :)
Sizga shu savol berib maqolamni yakunlayman
Redis single-threaded bo'lsa, bir vaqtda minglab so'rovni qanday boshqaradi va nima uchun bu arxitektura disk asosidagi bazalardan ko'ra ko'p hollarda tezroq ishlaydi?