ORTA
Key Expiration & Eviction Stratejileri
Key expire olunca tetiklenen event — cleanup logic, audit trail, veya cascade invalidation için kullanılır.
Kod örneği görünümü
Bu sayfadaki eşleşen örnekleri seçilen istemciye göre gösterir.
TTL Yönetimi
# TTL ata
SET key "value" EX 3600 # saniye
SET key "value" PX 3600000 # milisaniye
EXPIRE key 3600 # mevcut key'e TTL ekle
PEXPIRE key 3600000
EXPIREAT key 1748390400 # Unix timestamp
# TTL sorgula
TTL key # saniye (-1: TTL yok, -2: key yok)
PTTL key # milisaniye
# TTL kaldır
PERSIST key
# Atomik increment + expire (Lua ile — native INCREX komutu yoktur)
EVAL "local v = redis.call('INCR', KEYS[1]); redis.call('EXPIRE', KEYS[1], ARGV[1]); return v" 1 mycounter 60
public class ExpirationService
{
private readonly IDatabase _redis;
public ExpirationService(IConnectionMultiplexer mux)
=> _redis = mux.GetDatabase();
// Set + TTL (en yaygın)
public async Task SetWithExpiryAsync(string key, string value, TimeSpan ttl)
{
await _redis.StringSetAsync(key, value, ttl);
}
// Mevcut key'e TTL ekle/güncelle
public async Task SetExpiryAsync(string key, TimeSpan ttl)
{
await _redis.KeyExpireAsync(key, ttl);
}
// Belirli bir zaman (absolute expiration)
public async Task SetExpiryAtAsync(string key, DateTime expireAt)
{
await _redis.KeyExpireAsync(key, expireAt);
}
// Kalan TTL kontrol
public async Task<TimeSpan?> GetTtlAsync(string key)
{
return await _redis.KeyTimeToLiveAsync(key);
}
// Sliding expiration pattern
public async Task<string?> GetWithSlidingExpiryAsync(string key, TimeSpan slidingTtl)
{
var value = await _redis.StringGetAsync(key);
if (value.HasValue)
{
// Her okumada TTL yenile
await _redis.KeyExpireAsync(key, slidingTtl);
}
return value;
}
// Keyspace notifications ile expire dinle
public async Task SubscribeToExpirationsAsync(Action<string> onExpired)
{
// redis.conf: notify-keyspace-events Ex
var sub = _redis.Multiplexer.GetSubscriber();
await sub.SubscribeAsync(
RedisChannel.Pattern("__keyevent@0__:expired"),
(channel, key) => onExpired(key.ToString()));
}
// Atomik increment + expire (Lua script)
private static readonly LuaScript _incrExScript = LuaScript.Prepare(@"
local v = redis.call('INCR', @key)
if v == 1 then
redis.call('EXPIRE', @key, @ttl)
end
return v
");
public async Task<long> IncrementWithExpiryAsync(string key, int ttlSeconds = 60)
{
var result = await _redis.ScriptEvaluateAsync(_incrExScript, new
{
key = (RedisKey)key,
ttl = ttlSeconds
});
return (long)result;
}
}
Eviction Policies
| Policy | Davranış | Ne Zaman |
|---|---|---|
| noeviction | Yeni yazım hata verir | Veri kaybı kabul edilemez |
| allkeys-lru | En az kullanılanı sil | Genel cache |
| allkeys-lfu | En az frekansla kullanılanı sil | Hot data koruması |
| volatile-lru | TTL'li key'lerden LRU | TTL'siz key'ler korunmalı |
| volatile-lfu | TTL'li key'lerden LFU | TTL'siz key'ler korunmalı |
| volatile-ttl | En kısa TTL'yi sil | Doğal sıralama |
| allkeys-random | Rastgele sil | Uniform erişim |
Önerilen: Cache →
allkeys-lru. Persistent store →noeviction. Hot data koruması →allkeys-lfu.
Keyspace Notifications (Expired Event)
# redis.conf'ta etkinleştir (varsayılan kapalı):
notify-keyspace-events Ex
# E = keyevent, x = expired events
# Runtime'da:
CONFIG SET notify-keyspace-events Ex
// .NET'te expired key event'lerini dinle
public class KeyExpirationListener : BackgroundService
{
private readonly IConnectionMultiplexer _mux;
private readonly ILogger<KeyExpirationListener> _logger;
private readonly IServiceScopeFactory _scopeFactory;
public KeyExpirationListener(IConnectionMultiplexer mux,
ILogger<KeyExpirationListener> logger, IServiceScopeFactory scopeFactory)
{
_mux = mux;
_logger = logger;
_scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken ct)
{
var sub = _mux.GetSubscriber();
// db0'daki tüm expire event'lerini dinle
await sub.SubscribeAsync(
RedisChannel.Pattern("__keyevent@0__:expired"),
async (channel, expiredKey) =>
{
var key = expiredKey.ToString();
_logger.LogDebug("Key expired: {Key}", key);
// Prefix'e göre aksiyon al
if (key.StartsWith("session:"))
{
// Session expire → audit log
using var scope = _scopeFactory.CreateScope();
var audit = scope.ServiceProvider.GetRequiredService<IAuditService>();
await audit.LogSessionExpiredAsync(key);
}
else if (key.StartsWith("lock:"))
{
// Orphan lock temizlendi — monitoring metric
_logger.LogWarning("Lock expired (possible timeout): {Key}", key);
}
});
// Cancellation bekle
await Task.Delay(Timeout.Infinite, ct);
}
}
// DI kayıt
builder.Services.AddHostedService<KeyExpirationListener>();
Keyspace notification at-most-once'tır. Client disconnect anında gelen event kaybolur. Kritik iş logic'i için güvenme — sadece monitoring/audit amaçlı kullan. Garantili expiration logic için polling (SCAN + TTL check) tercih et.