Redis - Cache i kolejkowanie w aplikacjach webowych

Redis: Cache i kolejkowanie w aplikacjach webowych
Redis to potężna baza danych in-memory, która rewolucjonizuje sposób zarządzania danymi tymczasowymi i asynchronicznymi zadaniami w aplikacjach webowych. W tym artykule przedstawimy, jak efektywnie wykorzystać Redis do cache'owania i implementacji systemów kolejkowania.
Czym jest Redis?
Redis (Remote Dictionary Server) to open-source'owa baza danych typu key-value działająca w pamięci RAM. Charakteryzuje się:
- Błyskawiczną wydajnością - operacje w pamięci RAM
- Wszechstronnością - strings, hashes, lists, sets, sorted sets
- Trwałością - opcjonalny zapis na dysk
- Pub/Sub - wbudowany system komunikacji
- Atomowymi operacjami - bezpieczne operacje wielowątkowe
Redis jako cache - zwiększ wydajność o 100x
Podstawowe strategie cache'owania
1. Cache-Aside (Lazy Loading)
Najpopularniejszy wzorzec - aplikacja zarządza cache:
async function getUser(userId) {
// Sprawdź cache
const cached = await redis.get(`user:${userId}`);
if (cached) {
return JSON.parse(cached);
}
// Pobierz z bazy danych
const user = await db.users.findById(userId);
// Zapisz w cache (TTL 1h)
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
2. Write-Through Cache
Zapis jednocześnie do cache i bazy:
async function updateUser(userId, data) {
// Aktualizuj bazę
const user = await db.users.update(userId, data);
// Aktualizuj cache
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
return user;
}
3. Write-Behind (Write-Back)
Zapis do cache, asynchroniczny zapis do bazy:
async function updateUserAsync(userId, data) {
const user = { ...data, id: userId };
// Zapisz do cache natychmiast
await redis.setex(`user:${userId}`, 3600, JSON.stringify(user));
// Dodaj do kolejki zapisu
await redis.lpush('user:write:queue', JSON.stringify(user));
return user;
}
Zaawansowane techniki cache'owania
Cache invalidation
Strategia usuwania nieaktualnych danych:
// Invalidacja po aktualizacji
async function invalidateUserCache(userId) {
await redis.del(`user:${userId}`);
// Invalidacja powiązanych cache'y
await redis.del(`user:${userId}:posts`);
await redis.del(`user:${userId}:comments`);
}
// Pattern-based invalidation
async function invalidateUserCaches(userId) {
const keys = await redis.keys(`user:${userId}:*`);
if (keys.length > 0) {
await redis.del(...keys);
}
}
Cache warming
Wypełnianie cache przed wzrostem ruchu:
async function warmCache() {
const popularUsers = await db.users.findPopular(100);
const pipeline = redis.pipeline();
for (const user of popularUsers) {
pipeline.setex(
`user:${user.id}`,
3600,
JSON.stringify(user)
);
}
await pipeline.exec();
}
Redis jako system kolejkowania
Implementacja kolejki zadań
Podstawowa kolejka FIFO
// Producer - dodaj zadanie
async function enqueueJob(jobData) {
await redis.lpush('jobs:queue', JSON.stringify({
id: Date.now(),
data: jobData,
timestamp: new Date().toISOString()
}));
}
// Consumer - przetwarzaj zadania
async function processJobs() {
while (true) {
const job = await redis.brpop('jobs:queue', 0);
if (job) {
const jobData = JSON.parse(job[1]);
await handleJob(jobData);
}
}
}
Kolejka z priorytetami
async function enqueueWithPriority(jobData, priority = 'normal') {
const queue = `jobs:${priority}:queue`;
await redis.lpush(queue, JSON.stringify(jobData));
}
async function processWithPriority() {
const queues = [
'jobs:high:queue',
'jobs:normal:queue',
'jobs:low:queue'
];
while (true) {
for (const queue of queues) {
const job = await redis.rpop(queue);
if (job) {
await handleJob(JSON.parse(job));
break;
}
}
await sleep(100); // Prevent busy waiting
}
}
Delayed jobs - zaplanowane zadania
// Dodaj zadanie z opóźnieniem
async function scheduleJob(jobData, delaySeconds) {
const executeAt = Date.now() + (delaySeconds * 1000);
await redis.zadd(
'jobs:delayed',
executeAt,
JSON.stringify(jobData)
);
}
// Przetwarzaj zaplanowane zadania
async function processDelayedJobs() {
while (true) {
const now = Date.now();
const jobs = await redis.zrangebyscore(
'jobs:delayed',
0,
now,
'LIMIT',
0,
10
);
for (const job of jobs) {
await handleJob(JSON.parse(job));
await redis.zrem('jobs:delayed', job);
}
await sleep(1000);
}
}
Bull Queue - profesjonalna implementacja
Dla produkcyjnych systemów zalecamy użycie biblioteki Bull:
const Queue = require('bull');
// Utwórz kolejkę
const emailQueue = new Queue('email', {
redis: {
host: 'localhost',
port: 6379
}
});
// Dodaj zadanie
await emailQueue.add('sendWelcome', {
email: 'user@example.com',
name: 'John Doe'
}, {
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000
}
});
// Przetwarzaj zadania
emailQueue.process('sendWelcome', async (job) => {
const { email, name } = job.data;
await sendEmail(email, `Welcome ${name}!`);
});
// Obsługa błędów
emailQueue.on('failed', (job, err) => {
console.error(`Job ${job.id} failed:`, err);
});
Redis w .NET
Konfiguracja StackExchange.Redis
using StackExchange.Redis;
public class RedisService
{
private readonly IConnectionMultiplexer _redis;
private readonly IDatabase _db;
public RedisService(IConfiguration config)
{
_redis = ConnectionMultiplexer.Connect(
config.GetConnectionString("Redis")
);
_db = _redis.GetDatabase();
}
// Cache methods
public async Task<T?> GetAsync<T>(string key)
{
var value = await _db.StringGetAsync(key);
return value.HasValue
? JsonSerializer.Deserialize<T>(value!)
: default;
}
public async Task SetAsync<T>(
string key,
T value,
TimeSpan? expiry = null
)
{
var json = JsonSerializer.Serialize(value);
await _db.StringSetAsync(key, json, expiry);
}
// Queue methods
public async Task EnqueueAsync<T>(string queue, T item)
{
var json = JsonSerializer.Serialize(item);
await _db.ListLeftPushAsync(queue, json);
}
public async Task<T?> DequeueAsync<T>(string queue)
{
var value = await _db.ListRightPopAsync(queue);
return value.HasValue
? JsonSerializer.Deserialize<T>(value!)
: default;
}
}
Monitorowanie i optymalizacja
Metryki wydajności
Monitoruj kluczowe metryki Redis:
# Statystyki pamięci
redis-cli info memory
# Hit ratio cache
redis-cli info stats | grep keyspace
# Liczba połączeń
redis-cli info clients
# Wolne komendy
redis-cli slowlog get 10
Najlepsze praktyki
1. Używaj odpowiednich struktur danych
// ❌ Źle - string dla listy
await redis.set('user:follows', JSON.stringify(follows));
// ✅ Dobrze - Set
await redis.sadd('user:follows', ...follows);
2. Ustaw TTL dla wszystkich kluczy
// ❌ Źle - brak TTL
await redis.set('temp:data', value);
// ✅ Dobrze - z TTL
await redis.setex('temp:data', 3600, value);
3. Używaj pipeline dla wielu operacji
// ❌ Wolne - pojedyncze operacje
for (const item of items) {
await redis.set(item.key, item.value);
}
// ✅ Szybkie - pipeline
const pipeline = redis.pipeline();
for (const item of items) {
pipeline.set(item.key, item.value);
}
await pipeline.exec();
Checklista implementacji Redis
Przed wdrożeniem Redis upewnij się, że:
- [ ] Odpowiednia strategia cache'owania wybrana
- [ ] TTL ustawiony dla wszystkich tymczasowych kluczy
- [ ] Cache invalidation zaimplementowany
- [ ] Obsługa błędów połączenia
- [ ] Monitoring i alerting skonfigurowany
- [ ] Backup i persistence włączony (jeśli potrzebne)
- [ ] Connection pooling używany
- [ ] Limity pamięci ustawione
- [ ] Eviction policy skonfigurowana
- [ ] Klaster lub replikacja (dla HA)
Przypadki użycia
E-commerce
- Cache produktów i kategorii
- Koszyk zakupowy w sesji
- Kolejka zamówień do przetworzenia
- Rate limiting API
Social media
- Cache postów i profili użytkowników
- Real-time liczniki (likes, views)
- Pub/Sub dla powiadomień
- Feed użytkownika
SaaS
- Cache ustawień aplikacji
- Session storage
- Feature flags
- Analytics events queue
Podsumowanie
Redis to wszechstronne narzędzie, które:
- Przyspiesza aplikacje - cache zmniejsza obciążenie bazy
- Skaluje wydajność - obsługa milionów operacji/s
- Upraszcza architekturę - jedno narzędzie do wielu zadań
- Zwiększa niezawodność - asynchroniczne przetwarzanie
Właściwe wykorzystanie Redis może zwiększyć wydajność aplikacji nawet 100-krotnie i znacząco poprawić user experience.
Potrzebujesz wsparcia?
W MDS Software Solutions Group pomagamy w:
- Implementacji cache'owania z Redis
- Migracji z Memcached do Redis
- Optymalizacji wydajności aplikacji
- Implementacji systemów kolejkowania
- Audycie architektury
Skontaktuj się z nami, aby omówić Twój projekt!
Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.