要避免尖峰時,大量呼叫的 API 造成頻繁存取資料時,可以為常常重複取得的資料建立快取,使其可從快取直接取得資料,不必查詢資料庫。
簡介
以下用 .NET 的 IMemoryCache 建立單機的快取,如果要多個伺服器都使用相同快取,需考慮用 Redis 建立快取。
在分層式架構中,Presentation (展示) 層負責處理請求,可以將快取加入 Business (業務) 層,將 Persistence (持久) 層的資料暫存起來重複利用。
設定快取的步驟
1 在 Program.cs
註冊快取
var builder = WebApplication.CreateBuilder(args);
// 註冊記憶體快取
builder.Services.AddMemoryCache();
// 註冊 Service
builder.Services.AddScoped<IProductService, ProductService>();
var app = builder.Build();
app.Run();
2 建立 Service (Business) 層的快取
這裡的 ProductService 會先嘗試從快取讀取資料,若快取內沒有資料,則透過 Repository 查詢資料庫,並將結果存入快取。
public interface IProductService
{
Task<List<Product>> GetProductsAsync();
}
public class ProductService : IProductService
{
private readonly IMemoryCache _cache;
private readonly IProductRepository _productRepository;
public ProductService(IMemoryCache cache, IProductRepository productRepository)
{
_cache = cache;
_productRepository = productRepository;
}
public async Task<List<Product>> GetProductsAsync()
{
string cacheKey = "product_list";
// 嘗試從快取讀取
if (!_cache.TryGetValue(cacheKey, out List<Product> products))
{
// 如果快取沒有,則從資料庫查詢
products = await _productRepository.GetAllProductsAsync();
// 設定快取,有效時間 10 分鐘
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10));
_cache.Set(cacheKey, products, cacheOptions);
}
return products;
}
}
3 建立 Repository (Business) 層
public interface IProductRepository
{
Task<List<Product>> GetAllProductsAsync();
}
public class ProductRepository : IProductRepository
{
public async Task<List<Product>> GetAllProductsAsync()
{
// 模擬資料庫查詢
await Task.Delay(500); // 模擬延遲
return new List<Product>
{
new Product { Id = 1, Name = "產品 A" },
new Product { Id = 2, Name = "產品 B" }
};
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
4 Controller (Presentation) 層使用快取的 Service
[ApiController]
[Route("api/products")]
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public async Task<ActionResult<List<Product>>> GetProducts()
{
var products = await _productService.GetProductsAsync();
return Ok(products);
}
}
如果需要在多處設定快取,可以考慮建立自己的快取服務類別,統一管理快取邏輯,並減少重複的程式碼。