Chiến lược Caching đa tầng: Từ Browser đến Database cho ứng dụng hiệu năng cao
Posted on: 4/25/2026 2:17:49 PM
Table of contents
- 1. Vì sao caching là backbone của mọi hệ thống hiệu năng cao
- 2. Kiến trúc caching đa tầng — 4 lớp bảo vệ
- 3. Tầng 1: Browser Cache — zero network, zero cost
- 4. Tầng 2: CDN Edge Cache — giảm latency theo địa lý
- 5. Tầng 3: Application Cache — trái tim của caching strategy
- 6. Tầng 4: Database Cache — tối ưu ở nguồn dữ liệu
- 7. Cache Invalidation — "chỉ có hai thứ khó trong CS"
- 8. Cache Stampede & Thundering Herd — phòng thủ chủ động
- 9. Monitoring & Observability cho Cache
- 10. Production Checklist
- 11. Tổng kết
- 12. Tham khảo
1. Vì sao caching là backbone của mọi hệ thống hiệu năng cao
Trong bất kỳ cuộc phỏng vấn System Design nào, câu trả lời cho "làm sao để hệ thống nhanh hơn?" hầu như luôn bắt đầu bằng caching. Nhưng trong thực tế triển khai, caching không đơn giản chỉ là "thêm Redis". Một hệ thống thực sự hiệu năng cao triển khai caching ở nhiều tầng khác nhau, mỗi tầng giải quyết một lớp latency riêng biệt. Hiểu sai tầng nào nên cache gì sẽ dẫn tới lãng phí tài nguyên, data stale, hoặc tệ hơn — cache stampede làm sập cả hệ thống.
Bài viết này phân tích kiến trúc caching đa tầng từ góc nhìn thực chiến: 4 tầng cache từ browser tới database, 5 chiến lược invalidation, và cách .NET 10 với HybridCache mới giải quyết bài toán L1/L2 cache một cách triệt để.
2. Kiến trúc caching đa tầng — 4 lớp bảo vệ
Mỗi tầng cache đóng vai trò như một lớp bảo vệ, chặn request trước khi nó rơi xuống tầng đắt đỏ hơn phía dưới. Càng gần user, latency càng thấp nhưng dung lượng càng hạn chế và khả năng invalidation càng khó kiểm soát.
flowchart TB
User["👤 User / Browser"]
BCache["🖥️ Browser Cache
Cache-Control, ETag, Service Worker
~0ms latency"]
CDN["🌐 CDN Edge Cache
Cloudflare, CloudFront
~10-30ms latency"]
AppCache["⚡ Application Cache
IMemoryCache / HybridCache / Redis
~1-50ms latency"]
DB["🗄️ Database
Query Cache, Materialized View
~100-500ms latency"]
User -->|"Request"| BCache
BCache -->|"Cache Miss"| CDN
CDN -->|"Cache Miss"| AppCache
AppCache -->|"Cache Miss"| DB
DB -->|"Response"| AppCache
AppCache -->|"Response + Cache"| CDN
CDN -->|"Response + Cache"| BCache
BCache -->|"Response"| User
style User fill:#e94560,stroke:#fff,color:#fff
style BCache fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style CDN fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style AppCache fill:#f8f9fa,stroke:#2196F3,color:#2c3e50
style DB fill:#2c3e50,stroke:#fff,color:#fff
Hình 1: Kiến trúc caching đa tầng — request đi từ trên xuống, mỗi tầng "chặn" trước khi rơi xuống tầng đắt hơn
| Tầng | Latency | Dung lượng | Phạm vi | Invalidation |
|---|---|---|---|---|
| Browser Cache | ~0ms | ~50-300MB | Một user, một device | Khó (phụ thuộc TTL, user action) |
| CDN Edge | 10-30ms | TB-level | Tất cả user trong region | API purge, TTL, tag-based |
| Application | 1-50ms | GB-level | Tất cả request tới cluster | Chủ động (event, TTL, tag) |
| Database | 100-500ms | Disk-level | Query layer | Tự động khi data thay đổi |
3. Tầng 1: Browser Cache — zero network, zero cost
Browser cache là tầng cache nhanh nhất vì hoàn toàn không cần network request. Trình duyệt lưu response vào ổ đĩa/memory dựa trên HTTP headers mà server trả về.
3.1. Cache-Control — bộ não của browser cache
Header Cache-Control quyết định browser có được cache hay không, cache bao lâu, và có cần xác nhận lại với server không:
// Static assets — cache lâu, immutable
Cache-Control: public, max-age=31536000, immutable
// API response — cache ngắn, phải xác nhận
Cache-Control: private, max-age=0, must-revalidate
// HTML page — cache có điều kiện
Cache-Control: public, max-age=300, stale-while-revalidate=60
stale-while-revalidate — chiến lược "phục vụ cũ, cập nhật ngầm"
stale-while-revalidate=60 cho phép browser trả về bản cache cũ ngay lập tức cho user, đồng thời gửi request ngầm tới server để lấy bản mới. User thấy trang tải nhanh, bản mới sẽ sẵn sàng cho lần sau. Đây là pattern lý tưởng cho trang blog, danh mục sản phẩm — nơi dữ liệu thay đổi không thường xuyên nhưng vẫn cần cập nhật.
3.2. ETag — xác nhận thông minh không tải lại toàn bộ
Khi max-age hết hạn, browser gửi If-None-Match với ETag. Server so sánh: nếu nội dung chưa đổi thì trả về 304 Not Modified (vài byte) thay vì gửi lại toàn bộ response (có thể vài trăm KB). Tiết kiệm băng thông đáng kể cho API có payload lớn.
// Response đầu tiên
HTTP/1.1 200 OK
ETag: "a1b2c3d4e5"
Cache-Control: max-age=300
// Request tiếp theo sau khi cache hết hạn
GET /api/products HTTP/1.1
If-None-Match: "a1b2c3d4e5"
// Server: data chưa đổi
HTTP/1.1 304 Not Modified
3.3. Service Worker — cache layer có thể lập trình
Service Worker cho phép bạn viết logic cache hoàn toàn tùy biến bằng JavaScript. Phổ biến trong PWA, Service Worker chặn mọi fetch request và quyết định: lấy từ cache, từ network, hay kết hợp cả hai (stale-while-revalidate pattern ở tầng code).
// sw.js — Cache-first cho static, Network-first cho API
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/')) {
// Network-first cho API calls
event.respondWith(
fetch(event.request)
.then(response => {
const clone = response.clone();
caches.open('api-v1').then(cache =>
cache.put(event.request, clone));
return response;
})
.catch(() => caches.match(event.request))
);
} else {
// Cache-first cho static assets
event.respondWith(
caches.match(event.request)
.then(cached => cached || fetch(event.request))
);
}
});
4. Tầng 2: CDN Edge Cache — giảm latency theo địa lý
CDN đặt bản sao nội dung tại hàng trăm edge server trên toàn cầu. Khi user ở Việt Nam request, CDN trả về từ node Singapore thay vì từ origin server ở US — giảm latency từ 200ms xuống 20ms.
4.1. Cloudflare — cache rules thông minh miễn phí
Cloudflare cung cấp cache rules mạnh mẽ ngay ở Free plan. Bạn có thể cache theo path, query string, header, và thiết lập TTL khác nhau cho từng loại content:
# Ví dụ Cloudflare Cache Rules (qua Dashboard hoặc API)
# Rule 1: Cache static assets 1 năm
URI Path matches "/assets/*"
→ Cache eligible, Edge TTL: 365 days, Browser TTL: 365 days
# Rule 2: Cache API responses 5 phút
URI Path matches "/api/products*"
→ Cache eligible, Edge TTL: 300s, Browser TTL: 0
→ Cache Key: include query string
# Rule 3: Bypass cache cho authenticated
Cookie contains "auth_token"
→ Bypass cache
Cache Key — quyết định "giống nhau" hay "khác nhau"
Cache key xác định hai request có trả cùng một bản cache không. Mặc định CDN dùng URL + query string làm cache key. Nhưng nếu bạn phục vụ nội dung khác nhau theo header (ví dụ Accept-Language cho đa ngôn ngữ), phải thêm header đó vào cache key — nếu không, user Việt có thể nhận được bản English đã cache.
4.2. Cache Purge và Tag-based Invalidation
CDN cho phép purge cache bằng URL cụ thể, prefix (purge tất cả /api/products/*), hoặc theo cache tag. Cloudflare Enterprise và AWS CloudFront đều hỗ trợ tag-based purge — khi một sản phẩm thay đổi, purge tag product-123 sẽ xóa mọi response liên quan ở tất cả edge server trong vài giây.
sequenceDiagram
participant App as Application
participant CDN as CDN Edge
participant User as Users
App->>CDN: Response + Cache-Tag: product-123
CDN->>User: Serve cached response
Note over App: Product 123 updated
App->>CDN: Purge cache tag "product-123"
CDN->>CDN: Invalidate all entries with tag
User->>CDN: Next request
CDN->>App: Cache MISS → fetch fresh
App->>CDN: New response + Cache-Tag: product-123
CDN->>User: Serve fresh response
Hình 2: Flow tag-based cache invalidation — purge theo tag thay vì từng URL
5. Tầng 3: Application Cache — trái tim của caching strategy
Đây là tầng cache bạn có toàn quyền kiểm soát, nằm trong process hoặc ở distributed store. Trên .NET, hệ sinh thái caching đã phát triển qua ba thế hệ:
5.1. IMemoryCache — đơn giản nhưng có bẫy
IMemoryCache lưu object trực tiếp trong process memory, truy xuất ở tốc độ nanosecond. Phù hợp cho single-instance hoặc dữ liệu chỉ cần cache cục bộ (config, lookup table nhỏ).
// Đăng ký trong Program.cs
builder.Services.AddMemoryCache();
// Sử dụng
public class ProductService
{
private readonly IMemoryCache _cache;
private readonly IProductRepository _repo;
public async Task<Product?> GetByIdAsync(int id)
{
var key = $"product:{id}";
if (_cache.TryGetValue(key, out Product? cached))
return cached;
var product = await _repo.GetByIdAsync(id);
if (product is not null)
{
_cache.Set(key, product, new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10),
Size = 1 // nếu dùng SizeLimit
});
}
return product;
}
}
Bẫy Cache Stampede với IMemoryCache
Khi cache entry hết hạn, nếu có 100 request đồng thời, tất cả 100 đều thấy cache miss và đồng loạt gọi database. Với IMemoryCache, bạn phải tự xử lý bằng SemaphoreSlim hoặc Lazy<Task>. Đây chính là lý do HybridCache ra đời — stampede protection được tích hợp sẵn.
5.2. IDistributedCache — chia sẻ nhưng thô
Khi chạy nhiều instance (Kubernetes, load balancer), IMemoryCache ở mỗi pod là độc lập. User set cache ở Pod 1, nhưng request tiếp theo rơi vào Pod 2 → cache miss. IDistributedCache với Redis giải quyết vấn đề này:
// Program.cs
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
options.InstanceName = "myapp:";
});
// Sử dụng — API thô, phải tự serialize
public async Task<Product?> GetByIdAsync(int id)
{
var key = $"product:{id}";
var bytes = await _cache.GetAsync(key);
if (bytes is not null)
return JsonSerializer.Deserialize<Product>(bytes);
var product = await _repo.GetByIdAsync(id);
if (product is not null)
{
var json = JsonSerializer.SerializeToUtf8Bytes(product);
await _cache.SetAsync(key, json, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});
}
return product;
}
Nhược điểm rõ ràng: API làm việc với byte[], phải tự serialize/deserialize, không có stampede protection, và mỗi lần đọc cache đều qua network (dù hit).
5.3. HybridCache — tương lai của caching trên .NET
HybridCache (GA từ .NET 9, stable trong .NET 10) kết hợp ưu điểm của cả hai: L1 in-memory cho tốc độ, L2 distributed cho tính nhất quán, và thêm stampede protection + tag-based invalidation:
flowchart LR
Request["Request"] --> HC["HybridCache
GetOrCreateAsync"]
HC --> L1{"L1 Memory
Cache Hit?"}
L1 -->|"Hit"| Return["Return
~nanoseconds"]
L1 -->|"Miss"| L2{"L2 Redis
Cache Hit?"}
L2 -->|"Hit"| PopL1["Populate L1
Return ~ms"]
L2 -->|"Miss"| Factory["Factory Method
(DB Query)"]
Factory --> PopBoth["Populate L1 + L2
Return"]
style Request fill:#e94560,stroke:#fff,color:#fff
style HC fill:#2c3e50,stroke:#fff,color:#fff
style L1 fill:#f8f9fa,stroke:#4CAF50,color:#2c3e50
style L2 fill:#f8f9fa,stroke:#2196F3,color:#2c3e50
style Factory fill:#f8f9fa,stroke:#ff9800,color:#2c3e50
style Return fill:#4CAF50,stroke:#fff,color:#fff
style PopL1 fill:#4CAF50,stroke:#fff,color:#fff
style PopBoth fill:#4CAF50,stroke:#fff,color:#fff
Hình 3: HybridCache flow — L1 memory → L2 Redis → Factory, stampede protection ở mỗi tầng
// Program.cs — setup HybridCache
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(30),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
};
options.MaximumPayloadBytes = 1024 * 1024; // 1MB limit
});
// Thêm Redis làm L2 backend
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
});
// Sử dụng — API sạch, tự serialize, có stampede protection
public class ProductService(HybridCache cache, IProductRepository repo)
{
public async Task<Product?> GetByIdAsync(int id,
CancellationToken ct = default)
{
return await cache.GetOrCreateAsync(
$"product:{id}",
async cancel => await repo.GetByIdAsync(id, cancel),
new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromMinutes(30),
LocalCacheExpiration = TimeSpan.FromMinutes(5)
},
tags: ["products", $"product:{id}"],
cancellationToken: ct
);
}
public async Task UpdateAsync(Product product,
CancellationToken ct = default)
{
await repo.UpdateAsync(product, ct);
// Invalidate bằng tag — xóa tất cả entry liên quan
await cache.RemoveByTagAsync($"product:{product.Id}", ct);
}
}
| Tính năng | IMemoryCache | IDistributedCache | HybridCache |
|---|---|---|---|
| L1 (in-process) | ✅ | ❌ | ✅ |
| L2 (distributed) | ❌ | ✅ | ✅ |
| Stampede protection | ❌ | ❌ | ✅ |
| Tag-based invalidation | ❌ | ❌ | ✅ |
| Auto serialization | N/A (object ref) | ❌ (byte[]) | ✅ |
| Multi-instance safe | ❌ | ✅ | ✅ |
| Latency (hit) | ~ns | ~ms | ~ns (L1) / ~ms (L2) |
6. Tầng 4: Database Cache — tối ưu ở nguồn dữ liệu
Dù application cache chặn được phần lớn request, vẫn luôn có request rơi xuống database. Tối ưu tầng này giúp giảm latency cho cache miss và giảm áp lực khi cache bị invalidate hàng loạt.
6.1. Query Plan Cache
SQL Server và PostgreSQL đều cache execution plan cho các prepared statement. Dùng parameterized query thay vì string concatenation không chỉ chống SQL injection mà còn cho phép database tái sử dụng plan đã compile:
// ❌ Mỗi giá trị id tạo plan mới
var sql = $"SELECT * FROM Products WHERE Id = {id}";
// ✅ Plan được cache và tái sử dụng
var sql = "SELECT * FROM Products WHERE Id = @Id";
cmd.Parameters.AddWithValue("@Id", id);
6.2. Materialized View — pre-computed data
Thay vì chạy aggregate query phức tạp mỗi lần, Materialized View lưu kết quả đã tính sẵn. PostgreSQL hỗ trợ REFRESH MATERIALIZED VIEW CONCURRENTLY cho phép refresh không block read. SQL Server dùng Indexed View với tương tự mục đích.
-- PostgreSQL: Tạo materialized view cho dashboard stats
CREATE MATERIALIZED VIEW product_stats AS
SELECT
category_id,
COUNT(*) as total_products,
AVG(price) as avg_price,
MAX(updated_at) as last_updated
FROM products
GROUP BY category_id;
-- Refresh song song, không block read
REFRESH MATERIALIZED VIEW CONCURRENTLY product_stats;
7. Cache Invalidation — "chỉ có hai thứ khó trong CS"
Phil Karlton từng nói: "Chỉ có hai thứ khó trong khoa học máy tính: invalidation cache và đặt tên biến." Hiểu rõ 5 chiến lược invalidation giúp bạn chọn đúng pattern cho từng use case:
flowchart TB
subgraph CacheAside["Cache-Aside (Lazy Loading)"]
CA1["App kiểm tra cache"] --> CA2{"Hit?"}
CA2 -->|"Yes"| CA3["Trả về"]
CA2 -->|"No"| CA4["Query DB"]
CA4 --> CA5["Ghi vào cache"]
CA5 --> CA3
end
subgraph WriteThrough["Write-Through"]
WT1["App ghi data"] --> WT2["Ghi vào cache"]
WT2 --> WT3["Cache ghi vào DB"]
WT3 --> WT4["Confirm"]
end
subgraph WriteBehind["Write-Behind (Write-Back)"]
WB1["App ghi data"] --> WB2["Ghi vào cache"]
WB2 --> WB3["Confirm ngay"]
WB2 -.->|"Async"| WB4["Cache ghi DB sau"]
end
style CacheAside fill:#f8f9fa,stroke:#e94560
style WriteThrough fill:#f8f9fa,stroke:#4CAF50
style WriteBehind fill:#f8f9fa,stroke:#2196F3
Hình 4: Ba pattern chính của cache write strategy
| Pattern | Read perf | Write perf | Consistency | Phù hợp với |
|---|---|---|---|---|
| Cache-Aside | Chậm lần đầu (cold start) | Nhanh (chỉ ghi DB) | Eventual | 95% use case — mặc định nên dùng |
| Read-Through | Như Cache-Aside nhưng cache tự fetch | N/A | Eventual | Khi muốn cache layer tự quản lý |
| Write-Through | Luôn nhanh (data có sẵn) | Chậm hơn (ghi 2 nơi sync) | Strong | Data quan trọng cần nhất quán cao |
| Write-Behind | Luôn nhanh | Rất nhanh (async) | Eventual (rủi ro mất data) | High-write throughput, chấp nhận risk |
| Write-Around | Chậm lần đầu | Nhanh (bypass cache) | Eventual | Data ít đọc lại sau khi ghi |
Khung chọn pattern nhanh
Mặc định: Cache-Aside + TTL safety net. 95% ứng dụng web dùng pattern này là đủ.
Nếu cần strong consistency: Write-Through (ví dụ: inventory count, account balance).
Nếu write throughput là ưu tiên: Write-Behind (ví dụ: analytics event, view count).
Nếu data ít đọc lại: Write-Around (ví dụ: audit log, notification history).
8. Cache Stampede & Thundering Herd — phòng thủ chủ động
Cache stampede xảy ra khi một cache entry phổ biến hết hạn, hàng trăm request đồng thời phát hiện cache miss và đồng loạt query database. Đây là nguyên nhân hàng đầu gây sập hệ thống liên quan tới caching.
8.1. Các kỹ thuật phòng chống
// Kỹ thuật 1: Mutex/Lock — chỉ 1 request fetch, còn lại chờ
private static readonly SemaphoreSlim _lock = new(1, 1);
public async Task<Product?> GetWithLockAsync(int id)
{
var key = $"product:{id}";
if (_cache.TryGetValue(key, out Product? cached))
return cached;
await _lock.WaitAsync();
try
{
// Double-check sau khi acquire lock
if (_cache.TryGetValue(key, out cached))
return cached;
var product = await _repo.GetByIdAsync(id);
_cache.Set(key, product, TimeSpan.FromMinutes(30));
return product;
}
finally { _lock.Release(); }
}
// Kỹ thuật 2: Probabilistic Early Expiration
// Cache entry "tự giác" refresh trước khi hết hạn thực sự
public async Task<Product?> GetWithEarlyRefreshAsync(int id)
{
var key = $"product:{id}";
var entry = _cache.Get<CacheEntry<Product>>(key);
if (entry is not null)
{
var timeToExpiry = entry.ExpiresAt - DateTime.UtcNow;
var totalTtl = entry.Ttl;
// Xác suất refresh tăng dần khi gần hết hạn
var probability = Math.Exp(-timeToExpiry / totalTtl * 10);
if (Random.Shared.NextDouble() > probability)
return entry.Value;
}
// Fetch và cache lại
var product = await _repo.GetByIdAsync(id);
// ... set cache
return product;
}
HybridCache giải quyết stampede tự động
Với HybridCache.GetOrCreateAsync, khi 100 request đồng thời gọi cùng key, chỉ đúng 1 request chạy factory method (query DB), 99 request còn lại chờ kết quả. Không cần SemaphoreSlim, không cần double-check pattern. Đây là lý do chính để migrate từ IMemoryCache sang HybridCache.
9. Monitoring & Observability cho Cache
Cache không có monitoring giống như lái xe bịt mắt — bạn không biết mình đang tiết kiệm hay lãng phí. Ba metrics quan trọng nhất:
// Expose cache metrics qua OpenTelemetry
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddMeter("MyApp.Cache");
});
// Custom meter cho cache
public class CacheMetrics
{
private static readonly Meter _meter = new("MyApp.Cache");
private static readonly Counter<long> _hits =
_meter.CreateCounter<long>("cache.hits");
private static readonly Counter<long> _misses =
_meter.CreateCounter<long>("cache.misses");
private static readonly Histogram<double> _duration =
_meter.CreateHistogram<double>("cache.duration.ms");
public void RecordHit(string cacheLayer) =>
_hits.Add(1, new("layer", cacheLayer));
public void RecordMiss(string cacheLayer) =>
_misses.Add(1, new("layer", cacheLayer));
public void RecordDuration(string cacheLayer, double ms) =>
_duration.Record(ms, new("layer", cacheLayer));
}
10. Production Checklist
Trước khi deploy hệ thống có caching, hãy kiểm tra danh sách sau:
| # | Hạng mục | Ghi chú |
|---|---|---|
| 1 | TTL luôn được set | Không bao giờ cache vĩnh viễn — dù chỉ là config. Set cả absolute và sliding expiration. |
| 2 | Cache key có namespace | Prefix bằng tên service + version: myapp:v2:product:123. Tránh collision khi nhiều service dùng chung Redis. |
| 3 | Stampede protection | Dùng HybridCache hoặc implement mutex. Không để cache miss fan-out ra database. |
| 4 | Serialization format nhất quán | Chọn JSON hoặc MessagePack và giữ nguyên. Thay đổi format = invalidate toàn bộ cache. |
| 5 | Circuit breaker cho cache layer | Khi Redis down, fallback về database trực tiếp — không để timeout cache block request. |
| 6 | Monitoring đầy đủ | Hit rate, eviction rate, memory usage, latency. Alert khi hit rate < 80%. |
| 7 | Không cache PII/sensitive data | Hoặc nếu phải cache thì encrypt + TTL ngắn. Audit trail cho cache access. |
| 8 | Warm-up strategy | Sau deploy mới, cache lạnh. Cân nhắc pre-warm hot keys hoặc canary deploy. |
Quy tắc vàng của caching
Cache data được đọc nhiều hơn ghi. Nếu write/read ratio > 50%, caching có thể gây overhead nhiều hơn lợi ích — bạn sẽ tốn thời gian invalidation hơn thời gian tiết kiệm được từ cache hit. Trong trường hợp này, hãy tối ưu database trực tiếp (indexing, query optimization, connection pooling) thay vì thêm cache layer.
11. Tổng kết
Caching đa tầng không phải là "thêm Redis vào giữa app và DB". Đó là việc thiết kế một hệ thống phòng thủ nhiều lớp, mỗi lớp có vai trò, trade-off, và chiến lược invalidation riêng. Browser cache giảm network request, CDN giảm latency địa lý, application cache giảm database load, và database cache tối ưu ở nguồn dữ liệu.
Với HybridCache trong .NET 10, Microsoft đã giải quyết phần lớn complexity của application caching: L1/L2 tự động, stampede protection built-in, tag-based invalidation, và API sạch sẽ. Nếu bạn đang dùng IMemoryCache hoặc IDistributedCache riêng lẻ, đây là thời điểm tốt để migrate.
Nhớ rằng: cache tốt nhất là cache bạn không cần. Trước khi thêm cache, hãy hỏi: dữ liệu này có thực sự được đọc nhiều không? Query có thể tối ưu trực tiếp không? Cache chỉ nên là giải pháp sau khi bạn đã tối ưu ở tầng dữ liệu.
12. Tham khảo
- HybridCache library in ASP.NET Core — Microsoft Learn
- Hello HybridCache! Streamlining Cache Management — .NET Blog
- Caching in ASP.NET Core: Improving Application Performance — Milan Jovanović
- Distributed Caching in ASP.NET Core with Redis — CodeWithMukesh
- Caching Layers Explained: Browser, CDN, and App Caching — Technori
- Ultimate Guide to Caching 2026: Strategies and Best Practices — DragonflyDB
- Caching Strategies and How to Choose the Right One — CodeAhoy
- What is Caching and How it Works — AWS
Load Testing cho hệ thống phân tán — k6, NBomber và chiến lược kiểm thử hiệu năng
n8n — Nền tảng Tự động hóa Workflow AI mã nguồn mở cho Developer
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.