- Translated and revised architecture documents to enhance clarity and accessibility for both English and Vietnamese audiences. - Improved diagrams and descriptions for caching, data consistency, event-driven architecture, microservices communication, observability, and security architecture. - Ensured consistent formatting and terminology across all documents to facilitate better understanding and navigation. - Added quick tips and troubleshooting sections to assist developers in implementing and managing the architecture effectively.
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