# 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 ```mermaid graph TD Request[API Request] --> L1{L1 Cache
Memory} L1 -->|Hit| Return1[Return
< 1ms] L1 -->|Miss| L2{L2 Cache
Redis} L2 -->|Hit| WarmL1[Warm L1] WarmL1 --> Return2[Return
< 5ms] L2 -->|Miss| DB[(Database)] DB --> StoreL2[Store L2 + L1] StoreL2 --> Return3[Return
< 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 ```mermaid 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) ```csharp // 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 _logger; public MultiLayerCacheService( IMemoryCache l1Cache, IConnectionMultiplexer redis, ILogger logger) { _l1Cache = l1Cache; _redis = redis; _l2Cache = redis.GetDatabase(); _logger = logger; } public async Task GetAsync(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(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(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#)**: ```csharp // 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(CacheKeys.User("user_123")); var permissions = await _cache.GetAsync>(CacheKeys.UserPermissions("user_123")); ``` ## Chiến lược TTL ```mermaid graph LR subgraph "TTL Tiers" Short[Short TTL
60-300s
Frequently changing] Medium[Medium TTL
300-1800s
Moderately changing] Long[Long TTL
1800-3600s
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 ```mermaid 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**: ```typescript // 1. Invalidation single key async updateUser(userId: string, data: UpdateUserDto): Promise { 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 { 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 ```typescript // Preload dữ liệu thường xuyên truy cập async warmCache(): Promise { 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 ```mermaid graph TD subgraph "Redis Cluster" subgraph "Masters" M1[Redis Master 1
Slots: 0-5460] M2[Redis Master 2
Slots: 5461-10922] M3[Redis Master 3
Slots: 10923-16383] end subgraph "Slaves" S1[Redis Slave 1
Replica of M1] S2[Redis Slave 2
Replica of M2] S3[Redis Slave 3
Replica of M3] end M1 --> S1 M2 --> S2 M3 --> S3 Sentinel[Redis Sentinel
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**: ```yaml # 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**: ```typescript // 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 clients - `redis_used_memory_bytes` - Memory usage - `redis_memory_fragmentation_ratio` - Memory fragmentation - `redis_keyspace_hits_total` - Cache hits - `redis_keyspace_misses_total` - Cache misses - `redis_evicted_keys_total` - Evicted keys - `redis_expired_keys_total` - Expired keys - `redis_commands_processed_total` - Commands processed **Calculated Metrics**: ```promql # 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**: ```yaml # 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**: ```typescript // 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**: ```typescript // Health check cho Redis async function checkRedisHealth(): Promise { 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](./system-design.md) - Kiến trúc tổng thể với caching - [Data Consistency Patterns](./data-consistency-patterns.md) - 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-A` not `Node A`). - **Subgraphs**: Ensure `subgraph` names 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**