Redis Nedir ve Uygulamanızı Nasıl Hızlandırır?

Üretim ortamında bir uygulama yavaşlamaya başladığında, ilk bakacağınız yer genellikle veritabanıdır. Sorgular yavaş mı, index eksik mi, bağlantı havuzu dolmuş mu? Tüm bunları optimize edersiniz, bir süre rahat edersiniz. Sonra trafik artar, aynı sorun tekrar kapıya dayanır. İşte tam bu noktada Redis devreye girer ve oyunun kurallarını değiştirir.

Redis, “Remote Dictionary Server” kelimelerinin kısaltmasıdır ve özünde bir in-memory veri yapısı deposudur. Ama bunu salt bir önbellek çözümü olarak görmek büyük bir hata. Redis aynı zamanda mesaj kuyruğu, pub/sub sistemi, oturum yöneticisi, rate limiter ve çok daha fazlası olarak kullanılabilir. Ben Redis’i ilk kez kullandığımda sadece “hız için bir katman” olarak düşünmüştüm. Yıllar içinde bunun ne kadar dar bir bakış açısı olduğunu anladım.

Redis Nasıl Çalışır?

Redis, verilerini tamamen RAM üzerinde tutar. Bu, disk I/O’sunun neredeyse sıfıra indiği anlamına gelir. Geleneksel ilişkisel veritabanları bir sorgu çalıştırdığında disk okuma, B-tree traversal, buffer pool yönetimi gibi onlarca işlem yapar. Redis ise key’i alır, hash table’dan değeri çeker, gönderir. Saniyede 100.000’in üzerinde okuma/yazma işlemi yapabilmesi bu sayededir.

Redis’in single-threaded event loop mimarisi de önemli bir detay. Tek thread kullanması, race condition ve locking sorunlarını büyük ölçüde ortadan kaldırır. Redis 6.0 ile birlikte I/O threading geldi ama komut işleme hala tek thread üzerinden yürür. Bu basitlik, güvenilirlik sağlar.

Veri kalıcılığı konusunda iki seçeneğiniz var: RDB (snapshot) ve AOF (Append Only File). RDB belirli aralıklarla anlık görüntü alır, AOF ise her yazma işlemini log’a kaydeder. Production’da genellikle ikisini birlikte kullanırsınız.

Kurulum ve Temel Yapılandırma

Ubuntu üzerinde kurulum son derece basit:

sudo apt update
sudo apt install redis-server -y

# Servisi başlat ve etkinleştir
sudo systemctl enable redis-server
sudo systemctl start redis-server

# Bağlantıyı test et
redis-cli ping
# PONG dönmesi beklenir

Varsayılan yapılandırma production için yeterli değil. /etc/redis/redis.conf dosyasında mutlaka şu ayarları yapın:

# Dışarıdan erişimi kapat, sadece localhost
bind 127.0.0.1

# Şifre belirle (boş bırakma)
requirepass guclu_bir_sifre_buraya

# Maksimum bellek sınırı (sunucunuzun RAM'ine göre ayarlayın)
maxmemory 2gb

# Bellek dolduğunda ne yapacak: LRU algoritmasıyla en az kullanılanı sil
maxmemory-policy allkeys-lru

# TCP backlog
tcp-backlog 511

# Slow log eşiği (microsecond cinsinden, 10ms üzeri işlemleri logla)
slowlog-log-slower-than 10000
slowlog-max-len 128

Ayarları yaptıktan sonra servisi yeniden başlatın:

sudo systemctl restart redis-server

# Yapılandırmanın aktif olduğunu doğrula
redis-cli -a guclu_bir_sifre_buraya CONFIG GET maxmemory

Temel Veri Yapıları ve Kullanım Senaryoları

Redis’in gücü, sadece key-value saklamamasından gelir. String, List, Set, Sorted Set, Hash ve daha fazla veri yapısını destekler. Her birinin farklı bir kullanım alanı vardır.

String: En Basit Ama En Güçlü

redis-cli -a sifreniz

# Basit önbellekleme
SET kullanici:1001:profil '{"ad":"Ahmet","email":"[email protected]"}' EX 3600
GET kullanici:1001:profil

# Atomic counter (görüntülenme sayısı, like sayısı vb.)
INCR makale:5432:goruntulenme
INCRBY makale:5432:goruntulenme 10

# Süreli erişim tokeni
SET session:abc123def "kullanici_id:1001" EX 86400
TTL session:abc123def

EX parametresi saniye cinsinden TTL (Time To Live) belirler. Bu, önbelleğin kendiliğinden temizlenmesini sağlar. Bunu unutanları gördüm, Redis zamanla bir çöp yığınına döner.

Hash: Nesne Saklama

Kullanıcı profili gibi birden fazla alanı olan nesneler için Hash yapısını kullanın. Tüm nesneyi JSON olarak string’e seri hale getirmek yerine, sadece ihtiyacınız olan alanı çekebilirsiniz:

# Kullanıcı verisi kaydet
HSET kullanici:1001 ad "Ahmet Yılmaz" email "[email protected]" yas 32 sehir "Istanbul"

# Sadece email'i çek
HGET kullanici:1001 email

# Tüm alanları çek
HGETALL kullanici:1001

# Sadece belirli alanları çek
HMGET kullanici:1001 ad email

Sorted Set: Liderlik Tablosu

Oyun skorları, en popüler içerikler veya herhangi bir sıralama gereksinimi için Sorted Set biçilmiş kaftan:

# Oyun skoru ekle
ZADD oyun:liderlik 15420 "oyuncu:ahmet"
ZADD oyun:liderlik 22100 "oyuncu:mehmet"
ZADD oyun:liderlik 18750 "oyuncu:ayse"

# En yüksek 3 skoru getir
ZREVRANGE oyun:liderlik 0 2 WITHSCORES

# Ahmet'in sıralamasını öğren
ZREVRANK oyun:liderlik "oyuncu:ahmet"

Gerçek Dünya Senaryosu: E-ticaret Uygulamasında Önbellekleme

Bir e-ticaret platformunda çalışırken yaşadığım bir durumu anlatayım. Ana ürün listeleme sayfası, her yüklenişinde 23 ayrı SQL sorgusu çalıştırıyordu. Kategoriler, öne çıkan ürünler, fiyat filtreleri, stok bilgisi… Sayfa yükleme süresi ortalama 2.8 saniyeydi. Google PageSpeed’den tam anlamıyla kırmızı kart yiyorduk.

Çözüm Redis ile katmanlı bir önbellek stratejisi oldu:

# Python örneği (pseudocode değil, gerçek production kodu)
import redis
import json
from functools import wraps

r = redis.Redis(
    host='localhost',
    port=6379,
    password='sifreniz',
    decode_responses=True,
    socket_connect_timeout=2,
    socket_timeout=2
)

def redis_cache(ttl=300, prefix="cache"):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            cache_key = f"{prefix}:{func.__name__}:{str(args)}:{str(kwargs)}"
            
            # Önce Redis'e bak
            cached = r.get(cache_key)
            if cached:
                return json.loads(cached)
            
            # Redis'te yoksa fonksiyonu çalıştır
            result = func(*args, **kwargs)
            
            # Sonucu Redis'e yaz
            r.setex(cache_key, ttl, json.dumps(result))
            return result
        return wrapper
    return decorator

@redis_cache(ttl=600, prefix="urun")
def kategori_urunlerini_getir(kategori_id, sayfa=1):
    # Veritabanı sorgusu burada
    pass

Bu yaklaşımla sayfa yükleme süresi 2.8 saniyeden 180 milisaniyeye indi. Cache hit rate %94’e ulaştı. Veritabanı yükü dramatik biçimde düştü.

Rate Limiting ile Uygulama Güvenliği

API’lerinizi brute force ve DDoS saldırılarından korumak için Redis mükemmel bir rate limiter sağlar:

# Nginx ile entegre Redis rate limiting için lua scripti örneği
# /etc/nginx/lua/rate_limit.lua

local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)

local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
    return false
end

red:auth("sifreniz")

local ip = ngx.var.remote_addr
local key = "rate_limit:" .. ip
local limit = 100  -- dakikada 100 istek
local window = 60  -- 60 saniyelik pencere

local current = red:incr(key)
if tonumber(current) == 1 then
    red:expire(key, window)
end

if tonumber(current) > limit then
    ngx.status = 429
    ngx.say('{"error": "Too Many Requests"}')
    ngx.exit(429)
end

Aynı şeyi Python’da daha basit şekilde yapabilirsiniz:

def rate_limit_kontrol(ip_adresi, limit=100, pencere=60):
    key = f"rate_limit:{ip_adresi}"
    pipe = r.pipeline()
    pipe.incr(key)
    pipe.expire(key, pencere)
    sonuc = pipe.execute()
    
    istek_sayisi = sonuc[0]
    
    if istek_sayisi > limit:
        raise Exception(f"Rate limit aşıldı. {pencere} saniye bekleyin.")
    
    return True

Pipeline kullanımına dikkat edin. İki ayrı Redis komutu yerine tek bir network round-trip ile işi halleder. Yüksek trafikli uygulamalarda bu küçük detay büyük fark yaratır.

Session Yönetimi

Sticky session kullanarak load balancer arkasında oturum yönetimi yapmak bir kabustur. Redis bunu zarif biçimde çözer:

# PHP örneği için redis.conf
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=sifreniz&database=1"
session.gc_maxlifetime = 86400

# Node.js express-session ile kullanım
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const { createClient } = require('redis');

const redisClient = createClient({
    socket: {
        host: '127.0.0.1',
        port: 6379
    },
    password: 'sifreniz'
});

redisClient.connect().catch(console.error);

app.use(session({
    store: new RedisStore({ client: redisClient }),
    secret: 'uygulama-gizli-anahtariniz',
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: true,       // HTTPS zorunlu
        httpOnly: true,     // XSS koruması
        maxAge: 86400000    // 24 saat
    }
}));

Artık hangi sunucuya istek giderse gitsin, oturum verisi merkezi Redis’ten okunur. Ölçeklendirme sorunsuz hale gelir.

Redis Pub/Sub ile Gerçek Zamanlı İletişim

Mikro servis mimarisinde servisler arası iletişim için Redis Pub/Sub kullanmak oldukça pratik:

# Terminal 1: Abone ol
redis-cli -a sifreniz
SUBSCRIBE siparis-guncelleme

# Terminal 2: Mesaj gönder
redis-cli -a sifreniz
PUBLISH siparis-guncelleme '{"siparis_id":"12345","durum":"kargoya_verildi","zaman":"2024-01-15T14:30:00Z"}'

Bu yapı, sipariş durumu güncellemelerini anlık bildirim servisine iletmek, cache invalidation mesajları yaymak veya log agregasyonu için son derece işlevsel.

Performans İzleme ve Sorun Giderme

Redis’i kurup unutmak olmaz. Düzenli izleme şart:

# Anlık istatistikler
redis-cli -a sifreniz INFO stats

# Bellek kullanımı
redis-cli -a sifreniz INFO memory

# Bağlı istemci sayısı
redis-cli -a sifreniz CLIENT LIST | wc -l

# Yavaş sorguları listele
redis-cli -a sifreniz SLOWLOG GET 10

# Gerçek zamanlı komut izleme (production'da dikkatli kullanın!)
redis-cli -a sifreniz MONITOR

# Anahtar sayısını öğren
redis-cli -a sifreniz DBSIZE

# Bellek analizi (büyük key'leri bul)
redis-cli -a sifreniz --memkeys --memkeys-samples 64

İzlemeniz gereken kritik metrikler:

  • used_memory_rss: İşletim sisteminin Redis’e ayırdığı gerçek bellek
  • mem_fragmentation_ratio: 1.5 üzerindeyse bellek parçalanması var demektir
  • keyspace_hits/misses: Cache hit rate hesaplamak için kullanın
  • connected_clients: Bağlantı havuzu boyutunuzu buna göre ayarlayın
  • instantaneous_ops_per_sec: Anlık işlem hızı
  • evicted_keys: Bellek dolduğu için silinen key sayısı, bu değer yüksekse maxmemory’yi artırın

Prometheus ve Grafana kullananlar için redis_exporter’ı mutlaka kurun:

# redis_exporter kurulumu
wget https://github.com/oliver006/redis_exporter/releases/download/v1.55.0/redis_exporter-v1.55.0.linux-amd64.tar.gz
tar xzf redis_exporter-v1.55.0.linux-amd64.tar.gz
sudo mv redis_exporter /usr/local/bin/

# Servis olarak çalıştır
redis_exporter --redis.addr=redis://127.0.0.1:6379 --redis.password=sifreniz

Yaygın Hatalar ve Kaçınma Yolları

Yıllar içinde hem kendi yaptığım hem de başkalarının yaptığını gördüğüm hatalar var:

TTL koymayı unutmak: Her key’e TTL vermek bir alışkanlık olmalı. TTL’siz key’ler Redis’i zamanla doldurur. Var olan key’lere toplu TTL eklemek için:

# Belirli pattern'daki tüm key'lere TTL ekle
redis-cli -a sifreniz --scan --pattern "cache:*" | while read key; do
    redis-cli -a sifreniz EXPIRE "$key" 3600
done

KEYS komutunu production’da kullanmak: KEYS komutu tüm keyspace’i tarar ve Redis’i bloklar. Bunun yerine SCAN kullanın:

# YANLIS - production'da yapmayın
redis-cli -a sifreniz KEYS "kullanici:*"

# DOGRU - SCAN iteratif çalışır, bloklamaz
redis-cli -a sifreniz SCAN 0 MATCH "kullanici:*" COUNT 100

Bağlantı havuzu kullanmamak: Her işlem için yeni bağlantı açmak felaket. Her dil için connection pool implementasyonunu kullanın.

Büyük değerler saklamak: 100KB üzerindeki değerler Redis performansını düşürür. Büyük nesneleri sıkıştırın veya parçalayın.

Sonuç

Redis’i bir uygulamaya entegre etmek teknik açıdan basit, ancak doğru kullanmak deneyim ister. Salt bir önbellek olarak görmek onu haksızlığa uğratmak. Session yönetimi, rate limiting, pub/sub, liderlik tabloları, distributed lock gibi desenlerin hepsini tek bir araçla çözebiliyorsunuz.

Başlangıç için şu sırayı öneririm: önce en pahalı veritabanı sorgularınızı önbelleğe alın, sonra oturum yönetimini Redis’e taşıyın, ardından rate limiting ekleyin. Her adımda uygulama metriklerinizi izleyin. Sayılar sizi şaşırtacak.

Bir de şunu söyleyeyim: maxmemory’yi ayarlamadan Redis’i production’a almayın. Bellek dolduğunda Redis’in ne yapacağını önceden belirlemezseniz, uygulamanız yerine Redis karar verir. Bu hiçbir zaman güzel bitmez.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir