
Bellek içi veritabanları, verileri disk yerine öncelikle RAM'de depolar. Bu, disk G/Ç gecikmesini ortadan kaldırarak, geleneksel disk tabanlı veritabanlarından çok daha hızlı olan mikro saniyelik yanıt süreleri sağlar.
Bellek içi veritabanları, verileri disk yerine öncelikle RAM'de depolar. Bu, disk G/Ç gecikmesini ortadan kaldırarak, geleneksel disk tabanlı veritabanlarından çok daha hızlı olan mikro saniyelik yanıt süreleri sağlar.
Bellek içi veritabanları geleneksel veritabanlarının yerine geçmez; tamamlayıcıdırlar. Ultra düşük gecikme süresi, yüksek verim ve gerçek zamanlı veri işleme gerektiren kullanım durumlarında uzmandırlar: önbelleğe alma, oturum yönetimi, gerçek zamanlı analizler, skor tabloları, hız sınırlama ve mesaj aracılık.
| Görünüş | Bellek İçi Veritabanı | Disk Tabanlı Veritabanı |
|---|---|---|
| Birincil depolama | RAM | SSD/HDD |
| Okuma gecikmesi | < 1 ms (tipik 0,1 ms) | 1-10 ms (SSD), 5-20 ms (HDD) |
| Yazma verimi | 100K-1M+ işlem/sn | 1K-100K işlem/sn |
| Veri kalıcılığı | İsteğe bağlı (anlık görüntüler, AOF) | Her zaman ısrarcı |
| Kapasite | RAM sınırlı (GB-TB) | Disk sınırlı (TB'ler-PB'ler) |
| GB başına maliyet | ~$10-50/GB | ~0,10-1$/GB |
| Sorgu karmaşıklığı | Anahtar/değer çifti, basit yapılar | Karmaşık birleştirmeler, toplamalar |
| Dayanıklılık | Yapılandırılabilir (fsync takası) | Tam ASİT garantileri |
# Strings — most basic
SET user:100:name "Alice"
GET user:100:name # "Alice"
INCR page_counter # 1
INCRBY page_counter 5 # 6
# Lists — ordered, push/pop from ends
LPUSH notifications:101 "New message"
RPOP notifications:101 # "New message"
LLEN notifications:101 # Length
# Sets — unordered, unique members
SADD user:100:tags "redis" "python" "database"
SMEMBERS user:100:tags # All members
SISMEMBER user:100:tags "python" # 1 (true)
SINTER user:100:tags user:200:tags # Intersection
# Sorted Sets — scored, ordered
ZADD leaderboard 1000 "player1"
ZADD leaderboard 2500 "player2" 900 "player3"
ZREVRANGE leaderboard 0 2 WITHSCORES # Top 3
# 1) "player2" (2500)
# 2) "player3" (900)
# 3) "player1" (1000)
# Hashes — structured objects
HSET user:100 name "Alice" age 30 email "alice@example.com"
HGETALL user:100
HINCRBY user:100 login_count 1
# Streams — append-only log (like Kafka)
XADD events * type "login" user_id "100"
XRANGE events - + COUNT 10
# Geospatial — location-based
GEOADD locations 13.361389 38.115556 "Palermo"
GEORADIUS locations 15 37 100 km # Cities within 100km
| Mod | Nasıl Çalışır? | Dayanıklılık | Performans Etkisi |
|---|---|---|---|
| RDB (anlık görüntü) | Diske periyodik tam döküm | Son çöplük kaybedildi | Düşük (çatal + yazma) |
| AOF (yalnızca ekleme) | Günlüğe kaydedilen her yazma | Yapılandırılabilir: her zaman/sn/hayır | Orta |
| RDB + AOF | Her ikisi de birleştirildi | Yüksek | Orta |
| İsrar yok | Yalnızca RAM, disk G/Ç yok | Yeniden başlatmada yok | En iyi performans |
# redis.conf — persistence configuration
save 900 1 # RDB: save if 1 key changed in 900 seconds
save 300 10 # save if 10 keys changed in 300 seconds
save 60 10000 # save if 10000 keys changed in 60 seconds
appendonly yes # Enable AOF
appendfsync everysec # fsync every second (best trade-off)
Ana Kopya:
Client ──► Master (read/write)
│
┌────┴────┐
│ │
Replica1 Replica2 (read-only, failover targets)
Redis Sentinel — Yüksek Kullanılabilirlik:
┌──────────┐
│ Sentinel │ (monitors master, performs automatic failover)
└──────────┘
│
Client ──► Master ──► Replica1 ──► Replica2
│
(if master fails, Sentinel promotes a replica)
Redis Kümesi — Parçalama:
Client ──► Any node (auto-redirect to correct shard)
Shard 1 (Master A + Replica A')
Shard 2 (Master B + Replica B')
Shard 3 (Master C + Replica C')
Hash slots: 0-16383 distributed across shards
No central proxy — clients connect directly
Önbellek Kenarı (tembel yükleme):
def get_user(user_id: str) -> dict:
# Try cache first
cached = redis.get(f"user:{user_id}")
if cached:
return json.loads(cached)
# Cache miss — load from database
user = db.query("SELECT * FROM users WHERE id = %s", user_id)
# Store in cache with TTL
redis.setex(f"user:{user_id}", 3600, json.dumps(user))
return user
İçten Yazma:
def update_user(user_id: str, data: dict):
# Update database
db.execute("UPDATE users SET ... WHERE id = %s", data, user_id)
# Update cache immediately
redis.setex(f"user:{user_id}", 3600, json.dumps(data))
Arkasına Yazma (zaman uyumsuz):
def write_behind(user_id: str, data: dict):
# First to cache (instant)
redis.setex(f"user:{user_id}", 3600, json.dumps(data))
# Queue for async database write
redis.lpush("write_queue", json.dumps({
'user_id': user_id,
'data': data,
'timestamp': time.time()
}))
# Background worker processes the queue
def write_worker():
while True:
task = json.loads(redis.brpop("write_queue")[1])
db.execute("UPDATE users SET ... WHERE id = %s",
task['data'], task['user_id'])
| Strateji | Mekanizma | En İyisi |
|---|---|---|
| TTL tabanlı | Yaşam süresini ve otomatik sona erme süresini ayarlayın | Eski veriler kabul edilebilir |
| İçine yazma | Her yazmada önbelleği güncelle | Okuma-ağır, yazma-hafif |
| Arkasına yazma | DB'ye zaman uyumsuz yazma | Yazma ağırlıklı, nihai tutarlılık tamam |
| Önbelleğin geçersiz kılınması | Açık silme/güncelleme | Güçlü tutarlılık gerekli |
| Sürüm bazlı | Önbellek anahtarı sürümü içerir | Şema değişiklikleri |
| Politika | Açıklama | Kullanım Örneği |
|---|---|---|
| LRU (En Son Kullanılan) | Erişilen en eski anahtarı çıkar | Genel amaçlı |
| LFU (En Az Sık Kullanılan) | En az erişilen anahtarı çıkar | Kararlı erişim modelleri |
| TTL | Otomatik süresi dolan anahtarlar | Oturum verileri, geçici önbellek |
| Rastgele | Rastgele tahliye | Basit, öngörülebilir |
| Tahliye yok | Tam bellekte dönüş hatası | Kritik veriler kalmalı |
# Redis eviction policy
maxmemory 4gb
maxmemory-policy allkeys-lru # or: volatile-lru, allkeys-lfu, volatile-ttl
# Simple key-value (no persistence, no replication)
memcached -m 4096 -p 11211 -t 4
# Usage (similar to Redis but simpler)
set user:100 0 3600 15 # key, flags, ttl, byte_count
"Hello, World!"
get user:100
Redis ve Memcached:
| Özellik | Redis | Memcached |
|---|---|---|
| Veri yapıları | Dizeler, listeler, kümeler, karmalar, akışlar vb. | Yalnızca dizeler |
| İstikrar | RDB + AOF | Yok |
| Çoğaltma | Ana kopya + Küme | Yok |
| İşlemler | MULTI/EXEC, Lua | Yok |
| Bellek verimliliği | İyi (sıkıştırma seçenekleri var) | Daha iyi (daha düşük yük) |
| İş parçacığı geçirme | Tek iş parçacıklı (olay döngüsü) | Çok iş parçacıklı |
| Kullanım örneği | Genel amaçlı önbellek + veri yapıları | Yalnızca basit önbellek |
# Dragonfly is multi-threaded (faster on multi-core CPUs)
dragonfly --bind 0.0.0.0 --port 6379
# Same Redis protocol, same clients
redis-cli SET key value
redis-cli GET key
// Hazelcast — distributed cache with processing
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
HazelcastInstance hz = Hazelcast.newHazelcastInstance();
IMap<String, User> cache = hz.getMap("users");
cache.put("user:100", user);
User cached = cache.get("user:100");
// Distributed computing
cache.executeOnKey("user:100", entry -> {
User user = entry.getValue();
user.incrementLoginCount();
entry.setValue(user);
return null;
});
def create_session(user_id: str) -> str:
session_id = str(uuid.uuid4())
redis.setex(
f"session:{session_id}",
86400 * 7, # 7 days
json.dumps({
'user_id': user_id,
'created_at': time.time(),
'ip': request.remote_addr,
'user_agent': request.user_agent,
})
)
return session_id
def validate_session(session_id: str) -> dict | None:
data = redis.get(f"session:{session_id}")
if data:
return json.loads(data)
return None
import time
def check_rate_limit(user_id: str, max_requests: int = 100,
window_seconds: int = 60) -> bool:
key = f"ratelimit:{user_id}:{int(time.time()) // window_seconds}"
count = redis.incr(key)
if count == 1:
redis.expire(key, window_seconds)
return count <= max_requests
# Token bucket algorithm (more precise)
def token_bucket(user_id: str, capacity: int = 100,
refill_rate: float = 1.0) -> bool:
key = f"token_bucket:{user_id}"
now = time.time()
lua = """
local key = KEYS[1]
local now = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local refill_rate = tonumber(ARGV[3])
local bucket = redis.call('HMGET', key, 'tokens', 'last_refill')
local tokens = tonumber(bucket[1]) or capacity
local last_refill = tonumber(bucket[2]) or now
local elapsed = now - last_refill
tokens = math.min(capacity, tokens + elapsed * refill_rate)
if tokens >= 1 then
tokens = tokens - 1
redis.call('HMSET', key, 'tokens', tokens, 'last_refill', now)
return 1
else
return 0
end
"""
return bool(redis.eval(lua, 1, key, now, capacity, refill_rate))
def add_score(game_id: str, player: str, score: int):
redis.zadd(f"leaderboard:{game_id}", {player: score})
def get_top_players(game_id: str, n: int = 10) -> list[dict]:
results = redis.zrevrange(
f"leaderboard:{game_id}", 0, n - 1,
withscores=True
)
return [
{"player": player.decode(), "score": int(score)}
for player, score in results
]
def get_player_rank(game_id: str, player: str) -> int:
rank = redis.zrevrank(f"leaderboard:{game_id}", player)
return rank + 1 if rank is not None else None
# PUBLISH/SUBSCRIBE
# Publisher
redis.publish("notifications:global",
json.dumps({"type": "alert", "message": "System update at 2AM"}))
# Subscriber (separate process)
pubsub = redis.pubsub()
pubsub.subscribe("notifications:global")
for message in pubsub.listen():
if message['type'] == 'message':
process_notification(json.loads(message['data']))
# List-based queue (reliable)
redis.lpush("task_queue", json.dumps(task))
task = redis.brpop("task_queue") # Blocking pop
| Biçim | Serileştirme | Seri durumdan çıkarma | Boyut (1000 ürün) |
|---|---|---|---|
| Turşu (Python) | 1x | 1x | 100KB |
| JSON | 0,8x | 0,7x | 180KB |
| Mesaj Paketi | 0,6x | 0,5x | 130KB |
| Protobuf | 0,9x | 0,4x | 75KB |
| Özel ikili | 0,3x | 0,2x | 55KB |
# Without pipeline: N round-trips
for user_id in user_ids:
redis.get(f"user:{user_id}") # N network calls
# With pipeline: 1 round-trip
pipe = redis.pipeline()
for user_id in user_ids:
pipe.get(f"user:{user_id}")
results = pipe.execute() # Single network call
import redis
class RedisPool:
_instance = None
_pool = None
@classmethod
def get_connection(cls) -> redis.Redis:
if cls._pool is None:
cls._pool = redis.ConnectionPool(
host='redis-cluster.example.com',
port=6379,
max_connections=100,
socket_timeout=2,
retry_on_timeout=True,
health_check_interval=30,
)
return redis.Redis(connection_pool=cls._pool)
# Redis INFO
redis-cli INFO
# Key metrics to monitor:
connected_clients: 42
used_memory_human: 3.2G
used_memory_peak_human: 4.1G
total_commands_processed: 15238912
keyspace_hits: 984532
keyspace_misses: 5214
expired_keys: 11245
evicted_keys: 0
instantaneous_ops_per_sec: 4532
| Metrik | Uyarı | Kritik | Eylem |
|---|---|---|---|
| Bellek kullanımı | > Maksimum belleğin %80'i | > %95 | Ölçeği artırın, TTL'yi optimize edin |
| İsabet oranı | < %85 | < %70 | Önbelleğe alma stratejisini inceleyin |
| Çoğaltma gecikmesi | > 1 saniye | > 10 saniye | Ağı kontrol edin, çoğaltın |
| Çıkarılan anahtarlar | > 0 | > 100/saat | Belleği veya TTL'yi artırın |
Bellek içi veritabanları modern, yüksek performanslı uygulamalar için temel altyapıdır:
Need sub-millisecond latency?
├─ Yes
│ Q: Complexity of operations?
│ ├─ Simple key-value: Memcached
│ ├─ Data structures, persistence: Redis
│ └─ Distributed computing: Hazelcast/Ignite
└─ No
Q: Data fits in memory?
├─ Yes: Consider in-memory for performance
└─ No: Disk-based (PostgreSQL, etc.)
Bellek içi veritabanları, veritabanlarını değiştirmekle ilgili değildir; her şeyi daha hızlı hale getirmekle ilgilidir.
Henüz onaylı yorum yok. Yeni yanıtlar moderasyon bekleyebilir.