19 KiB
19 KiB
Kiến trúc Caching
Chiến lược caching nhiều tầng để tối ưu hiệu suất
Sơ đồ Tổng quan
graph TD
Request[API Request] --> L1{L1 Cache<br/>Memory}
L1 -->|Hit| Return1[Return<br/>< 1ms]
L1 -->|Miss| L2{L2 Cache<br/>Redis}
L2 -->|Hit| WarmL1[Warm L1]
WarmL1 --> Return2[Return<br/>< 5ms]
L2 -->|Miss| DB[(Database)]
DB --> StoreL2[Store L2 + L1]
StoreL2 --> Return3[Return<br/>< 50ms]
classDef memory fill:#1b5e20,stroke:#2e7d32,color:#fff
classDef redis fill:#e65100,stroke:#ef6c00,color:#fff
classDef db fill:#212121,stroke:#424242,color:#fff
classDef default fill:#202020,stroke:#505050,color:#fff
class L1,Return1,WarmL1 memory
class L2,Return2,StoreL2 redis
class DB,Return3 db
Bối cảnh Hệ thống
C4Context
title Sơ đồ Bối cảnh Hệ thống Caching
System(service, "Microservice", "Client service using cache")
System_Ext(db, "Neon PostgreSQL", "Primary database")
Boundary(caching, "Caching Layer") {
System(l1, "L1 Cache", "In-memory NodeCache")
System(l2, "L2 Cache", "Redis Cluster")
}
Rel(service, l1, "Reads/Writes", "In-process")
Rel(service, l2, "Reads/Writes", "Redis Protocol")
Rel(l1, l2, "Fills from", "On miss")
Rel(l2, db, "Cache aside", "On miss")
UpdateElementStyle(service, $fontColor="white", $bgColor="#1a237e", $borderColor="#3949ab")
UpdateElementStyle(db, $fontColor="white", $bgColor="#212121", $borderColor="#424242")
UpdateElementStyle(l1, $fontColor="white", $bgColor="#1b5e20", $borderColor="#2e7d32")
UpdateElementStyle(l2, $fontColor="white", $bgColor="#e65100", $borderColor="#ef6c00")
Mô tả Bối cảnh
- Service: Giao tiếp trực tiếp với L1 Cache (in-memory) để đạt độ trễ thấp nhất.
- L1 Cache: Cache cục bộ, không chia sẻ, tự động hết hạn (TTL ngắn).
- L2 Cache: Redis cluster chia sẻ, giữ dữ liệu lâu dài hơn và đồng bộ giữa các instances.
- Database: Nguồn dữ liệu gốc (source of truth), chỉ được truy cập khi cache miss.
Mô tả Kiến trúc
Caching Nhiều Tầng
Nền tảng GoodGo sử dụng caching 2 tầng để tối ưu hiệu suất:
L1 Cache (Memory):
- In-memory cache trên mỗi service instance
- Truy cập rất nhanh (< 1ms)
- Dung lượng giới hạn (10k keys mặc định)
- TTL ngắn (60 giây mặc định, tối đa 5 phút)
- Không share giữa instances
L2 Cache (Redis):
- Shared distributed cache
- Truy cập nhanh (< 5ms)
- Dung lượng lớn
- TTL dài hơn (configurable, thường 5-15 phút)
- Share giữa tất cả service instances
Cache Flow:
Request → L1 → L2 → Database
↓ ↓ ↓ ↓
40-50% 80-90% 10-20% Cache miss
hit rate hit rate rate
Triển khai Cache
Multi-Layer Cache Service (.NET)
// EN: Multi-layer cache implementation
// VI: Triển khai cache đa lớp
public class MultiLayerCacheService : ICacheService
{
private readonly IMemoryCache _l1Cache;
private readonly IConnectionMultiplexer _redis;
private readonly IDatabase _l2Cache;
private readonly ILogger<MultiLayerCacheService> _logger;
public MultiLayerCacheService(
IMemoryCache l1Cache,
IConnectionMultiplexer redis,
ILogger<MultiLayerCacheService> logger)
{
_l1Cache = l1Cache;
_redis = redis;
_l2Cache = redis.GetDatabase();
_logger = logger;
}
public async Task<T?> GetAsync<T>(string key, CancellationToken ct = default)
{
// L1: Memory cache check
if (_l1Cache.TryGetValue(key, out T? l1Value))
{
_logger.LogDebug("L1 cache hit for key: {Key}", key);
return l1Value;
}
// L2: Redis cache check
var l2Value = await _l2Cache.StringGetAsync(key);
if (!l2Value.IsNullOrEmpty)
{
_logger.LogDebug("L2 cache hit for key: {Key}", key);
var parsed = JsonSerializer.Deserialize<T>(l2Value!);
// Warm L1 cache
_l1Cache.Set(key, parsed, TimeSpan.FromMinutes(1));
return parsed;
}
_logger.LogDebug("Cache miss for key: {Key}", key);
return default;
}
public async Task SetAsync<T>(string key, T value, TimeSpan? ttl = null, CancellationToken ct = default)
{
var expiry = ttl ?? TimeSpan.FromMinutes(5);
var l1Expiry = TimeSpan.FromMinutes(Math.Min(expiry.TotalMinutes, 5));
// L1: Memory cache (max 5 min)
_l1Cache.Set(key, value, l1Expiry);
// L2: Redis cache
var json = JsonSerializer.Serialize(value);
await _l2Cache.StringSetAsync(key, json, expiry);
}
public async Task RemoveAsync(string key, CancellationToken ct = default)
{
_l1Cache.Remove(key);
await _l2Cache.KeyDeleteAsync(key);
}
}
Quy ước Đặt tên Key
Pattern: {service}:{entity}:{identifier}:{sub-resource}
Ví dụ (C#):
// Cache key constants
public static class CacheKeys
{
public static string User(string userId) => $"iam:user:{userId}";
public static string UserPermissions(string userId) => $"iam:user:{userId}:permissions";
public static string UserRoles(string userId) => $"iam:user:{userId}:roles";
public static string Session(string sessionId) => $"iam:session:{sessionId}";
public static string UserQuota(string userId) => $"storage:quota:{userId}";
}
// Sử dụng
var user = await _cache.GetAsync<UserDto>(CacheKeys.User("user_123"));
var permissions = await _cache.GetAsync<List<string>>(CacheKeys.UserPermissions("user_123"));
Chiến lược TTL
graph LR
subgraph "TTL Tiers"
Short[Short TTL<br/>60-300s<br/>Frequently changing]
Medium[Medium TTL<br/>300-1800s<br/>Moderately changing]
Long[Long TTL<br/>1800-3600s<br/>Rarely changing]
end
Short --> Permissions[User Permissions]
Short --> Sessions[Session Data]
Medium --> UserProfiles[User Profiles]
Medium --> OrgData[Organization Data]
Long --> Config[Static Config]
Long --> RefData[Reference Data]
classDef tier fill:#202020,stroke:#505050,color:#fff
classDef short fill:#b71c1c,stroke:#f44336,color:#fff
classDef medium fill:#e65100,stroke:#ef6c00,color:#fff
classDef long fill:#1b5e20,stroke:#2e7d32,color:#fff
class Short short
class Medium medium
class Long long
class Permissions,Sessions,UserProfiles,OrgData,Config,RefData tier
Hướng dẫn TTL:
| Loại Dữ liệu | TTL | Lý do |
|---|---|---|
| User permissions | 5 min | Nhạy cảm bảo mật |
| Session data | Varies | Dựa trên độ dài session |
| User profiles | 10 min | Tần suất cập nhật vừa phải |
| Organization data | 15 min | Cập nhật không thường xuyên |
| Static config | 30-60 min | Rất ổn định |
| Reference data | 1-2 hours | Hầu như không thay đổi |
Vô hiệu hóa Cache
sequenceDiagram
participant API
participant Service
participant Cache
participant DB
API->>Service: Update User
Service->>DB: UPDATE user
DB-->>Service: Success
Service->>Cache: Invalidate user:123
Service->>Cache: Invalidate user:123:permissions
Service->>Cache: Invalidate user:123:roles
Cache-->>Service: Cleared
Service-->>API: Success
Note over Service,Cache: Next request will fetch fresh data
Chiến lược Invalidation:
// 1. Invalidation single key
async updateUser(userId: string, data: UpdateUserDto): Promise<User> {
const user = await userRepository.update(userId, data);
// Vô hiệu hóa user cache
await cache.del(cacheKeys.user(userId));
return user;
}
// 2. Invalidation theo pattern
async updateUserRole(userId: string, roleId: string): Promise<void> {
await userRoleRepository.assign(userId, roleId);
// Vô hiệu hóa tất cả cache liên quan đến user
await cache.invalidatePattern(`iam:user:${userId}:*`);
}
// 3. Invalidation theo thời gian (TTL expiry)
// Tự động xử lý bởi cache
Làm ấm Cache
// Preload dữ liệu thường xuyên truy cập
async warmCache(): Promise<void> {
logger.info('Starting cache warming');
// Làm ấm user permissions cho active users
const activeUsers = await userRepository.findActive({ limit: 1000 });
for (const user of activeUsers) {
const permissions = await rbacService.getUserPermissions(user.id);
await cache.set(
cacheKeys.userPermissions(user.id),
permissions,
300 // 5 phút
);
}
logger.info('Cache warming completed', { count: activeUsers.length });
}
// Chạy khi service khởi động
warmCache().catch(err => logger.error('Cache warming failed', { err }));
Quyết định Thiết kế
Quyết định 1: Multi-layer Caching (L1 + L2)
Bối cảnh: Cần giảm tải cho Redis và đạt độ trễ cực thấp cho dữ liệu hot. Quyết định: Sử dụng kết hợp L1 (NodeCache) và L2 (Redis). Hậu quả:
- ✅ Độ trễ < 1ms cho 40-50% requests.
- ✅ Giảm network traffic tới Redis.
- ❌ Phức tạp trong đồng bộ (L1 có thể stale trong thời gian ngắn).
Đặc điểm Hiệu suất
Mục tiêu Hiệu suất
| Chỉ số | Mục tiêu | Ghi chú |
|---|---|---|
| L1 Hit Latency | < 0.5ms | In-memory lookup |
| L2 Hit Latency | < 5ms | Network RTT + Redis processing |
| Combine Hit Rate | > 90% | L1 + L2 combined |
| L1 Capacity | 10k items | Per instance limit to protect heap |
| Cache Warmup Time | < 30s | At service startup |
Cân nhắc Bảo mật
Bảo mật Cache
- Encryption: Dữ liệu nhạy cảm (PII) PHẢI được mã hóa trước khi lưu vào L2 Redis (AES-256). L1 có thể lưuplaintext vì nằm trong memory process (trừ khi memory dump).
- Isolation: Redis instance được bảo vệ bằng mật khẩu và Network Policy (chỉ allow traffic từ nội bộ K8s).
- TLS: Kết nối tới Redis qua TLS 1.2+.
- Data Sanitization: Không cache toàn bộ user object nếu chứa password hash hoặc secrets.
Triển khai
graph TD
subgraph "Redis Cluster"
subgraph "Masters"
M1[Redis Master 1<br/>Slots: 0-5460]
M2[Redis Master 2<br/>Slots: 5461-10922]
M3[Redis Master 3<br/>Slots: 10923-16383]
end
subgraph "Slaves"
S1[Redis Slave 1<br/>Replica of M1]
S2[Redis Slave 2<br/>Replica of M2]
S3[Redis Slave 3<br/>Replica of M3]
end
M1 --> S1
M2 --> S2
M3 --> S3
Sentinel[Redis Sentinel<br/>3 nodes]
Sentinel -.->|Monitor| M1
Sentinel -.->|Monitor| M2
Sentinel -.->|Monitor| M3
end
subgraph "Services"
Service1[Service A]
Service2[Service B]
Service3[Service C]
end
Service1 --> M1
Service1 --> M2
Service1 --> M3
Service2 --> M1
Service2 --> M2
Service2 --> M3
Service3 --> M1
Service3 --> M2
Service3 --> M3
classDef master fill:#e65100,stroke:#ef6c00,color:#fff
classDef slave fill:#f57c00,stroke:#e65100,color:#fff
classDef sentinel fill:#4a148c,stroke:#7b1fa2,color:#fff
classDef service fill:#1a237e,stroke:#3949ab,color:#fff
classDef default fill:#202020,stroke:#505050,color:#fff
class M1,M2,M3 master
class S1,S2,S3 slave
class Sentinel sentinel
class Service1,Service2,Service3 service
Chiến lược Triển khai
Redis Cluster Configuration:
- Mode: Cluster mode với 3 masters + 3 slaves
- Replication: Mỗi master có 1 slave cho high availability
- Sentinel: 3-node Sentinel ensemble cho automatic failover
- Sharding: 16384 hash slots phân chia đều giữa 3 masters
- Persistence: RDB snapshots mỗi 5 phút, AOF disabled (performance)
Resource Allocation:
| Component | CPU | Memory | Disk | Replicas |
|---|---|---|---|---|
| Redis Master | 1 core | 2GB | 10GB SSD | 3 |
| Redis Slave | 1 core | 2GB | 10GB SSD | 3 |
| Sentinel | 500m | 512MB | 5GB | 3 |
Redis Configuration:
# redis.conf
maxmemory 2gb
maxmemory-policy allkeys-lru # Evict least recently used keys
timeout 300 # Close idle connections after 5min
tcp-keepalive 60
save 300 10 # RDB snapshot every 5min if 10+ keys changed
appendonly no # Disable AOF for performance
# Cluster config
cluster-enabled yes
cluster-node-timeout 5000
cluster-replica-validity-factor 0
High Availability:
- Automatic failover với Redis Sentinel
- Slave promotion khi master fails
- Client-side retry logic
- Connection pooling (max 50 connections per service)
Scaling Strategy:
- Vertical: Tăng memory per node (2GB → 4GB → 8GB)
- Horizontal: Thêm master nodes (3 → 5 → 7)
- Read Scaling: Route reads to slaves
- Monitoring: Auto-alert khi memory usage > 80%
Giám sát & Khả năng quan sát
Chỉ số Chính
Cache Performance Metrics:
// Custom metrics cho cache performance
import { Counter, Histogram, Gauge } from 'prom-client';
export const cacheHits = new Counter({
name: 'cache_hits_total',
labelNames: ['layer', 'key_prefix'] // layer: l1/l2, key_prefix: user/session/etc
});
export const cacheMisses = new Counter({
name: 'cache_misses_total',
help: 'Tổng số cache misses',
labelNames: ['key_prefix']
});
export const cacheLatency = new Histogram({
name: 'cache_operation_duration_seconds',
help: 'Thời gian thực hiện cache operation',
labelNames: ['operation', 'layer'], // operation: get/set/del
buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1]
});
export const cacheSize = new Gauge({
name: 'cache_size_bytes',
help: 'Kích thước cache (bytes)',
labelNames: ['layer']
});
export const cacheEvictions = new Counter({
name: 'cache_evictions_total',
help: 'Tổng số cache evictions',
labelNames: ['layer', 'reason'] // reason: ttl_expired/memory_full
});
Redis Metrics:
redis_connected_clients- Connected clientsredis_used_memory_bytes- Memory usageredis_memory_fragmentation_ratio- Memory fragmentationredis_keyspace_hits_total- Cache hitsredis_keyspace_misses_total- Cache missesredis_evicted_keys_total- Evicted keysredis_expired_keys_total- Expired keysredis_commands_processed_total- Commands processed
Calculated Metrics:
# Cache hit rate
rate(cache_hits_total[5m]) / (rate(cache_hits_total[5m]) + rate(cache_misses_total[5m]))
# L1 hit rate
rate(cache_hits_total{layer="l1"}[5m]) / rate(cache_hits_total[5m])
# L2 hit rate
rate(cache_hits_total{layer="l2"}[5m]) / rate(cache_hits_total[5m])
# Average cache latency
histogram_quantile(0.95, cache_operation_duration_seconds_bucket)
# Memory usage percentage
redis_used_memory_bytes / redis_maxmemory_bytes * 100
Alerting Rules:
# Quy tắc cảnh báo cho cache
groups:
- name: cache_alerts
interval: 30s
rules:
# Low cache hit rate
- alert: LowCacheHitRate
expr: |
rate(cache_hits_total[5m]) /
(rate(cache_hits_total[5m]) + rate(cache_misses_total[5m])) < 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "Tỷ lệ cache hit thấp"
description: "Tỷ lệ cache hit là {{ $value | humanizePercentage }}"
# High memory usage
- alert: HighRedisMemoryUsage
expr: redis_used_memory_bytes / redis_maxmemory_bytes > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Sử dụng bộ nhớ Redis cao"
description: "Bộ nhớ Redis sử dụng là {{ $value | humanizePercentage }}"
# High eviction rate
- alert: HighEvictionRate
expr: rate(redis_evicted_keys_total[5m]) > 100
for: 5m
labels:
severity: warning
annotations:
summary: "Tỷ lệ cache eviction cao"
description: "Tỷ lệ eviction là {{ $value }}/giây"
# Redis down
- alert: RedisDown
expr: redis_up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Redis bị down"
# High replication lag
- alert: HighReplicationLag
expr: redis_replication_lag_seconds > 5
for: 2m
labels:
severity: warning
annotations:
summary: "Độ trễ replication cao"
description: "Độ trễ replication là {{ $value }}s"
Dashboards:
- Cache Overview: Hit rate, miss rate, latency, size
- Redis Cluster: Memory usage, connections, commands/sec
- Performance: L1 vs L2 hit rates, operation latency
- Evictions: Eviction rate, reasons, trends
Logging:
// Structured logging cho cache operations
logger.debug('Cache operation', {
operation: 'get',
layer: 'l1',
key: cacheKey,
hit: true,
latency: duration,
correlationId: req.correlationId
});
logger.warn('Cache eviction', {
layer: 'l2',
reason: 'memory_full',
evictedKeys: count,
memoryUsage: usagePercent
});
logger.error('Cache error', {
operation: 'set',
layer: 'l2',
error: error.message,
key: cacheKey
});
Health Checks:
// Health check cho Redis
async function checkRedisHealth(): Promise<boolean> {
try {
await redis.ping();
const info = await redis.info('memory');
const memoryUsage = parseMemoryUsage(info);
return memoryUsage < 0.9; // Healthy if < 90% memory
} catch (error) {
logger.error('Redis health check failed', { error });
return false;
}
}
Tài liệu Liên quan
- System Design - Kiến trúc tổng thể với caching
- Data Consistency Patterns - Cache invalidation patterns
Cập nhật Lần cuối: 2026-01-14
Tác giả: GoodGo Architecture Team
Quick Tips
Mermaid Common Issues
- Arrow Syntax: Use
-->for solid arrows,-.->for dotted arrows. - Node IDs: Avoid spaces/special chars in IDs (e.g.,
Node-AnotNode A). - Subgraphs: Ensure
subgraphnames are unique and descriptive.
Color Pattern Quick Reference
| Element | Dark Color | Text Color |
|---|---|---|
| Service (Blue) | #1a237e |
#ffffff |
| Storage (Gray) | #212121 |
#ffffff |
| Cache L2 (Orange) | #e65100 |
#ffffff |
| Cache L1 (Green) | #1b5e20 |
#ffffff |
| Monitoring (Purple) | #4a148c |
#ffffff |
Visual Indicators
- ✅ Recommended / Khuyên dùng
- ❌ Not Recommended / Không khuyên dùng
- ⚠️ Warning / Cảnh báo