Trong quá trình phát triển ứng dụng Optimizely CMS, các kiến trúc sư và lập trình viên thường đối mặt với câu hỏi: "Nên sử dụng Optimizely (Episerver) Find hay truy vấn database trực tiếp thông qua ContentLoader?" Đây không chỉ là vấn đề hiệu suất mà còn là quyết định kiến trúc ảnh hưởng đến khả năng mở rộng và chi phí vận hành.
Bài viết này phân tích một case study thực tế: tối ưu hóa truy vấn ProductCatalogBlock trong hệ thống thương mại điện tử quy mô nhỏ (khoảng 25 sản phẩm mỗi danh mục).
Case Study: Tối ưu hóa Catalog Sản phẩm
Bối cảnh Dự án
Chúng tôi quản lý một hệ thống đa trang web (multi-site) với các danh mục sản phẩm. Mỗi danh mục có khoảng 25 ProductCatalogBlock để hiển thị thông tin. Ban đầu, đội ngũ đã sử dụng Optimizely Find với giả định rằng công cụ tìm kiếm luôn nhanh hơn truy vấn truyền thống.
Giải pháp dựa trên Find
Sử dụng interface IClient để lọc dữ liệu đã được index trên cloud:
public List<ProductCatalogBlock> GetProductsByCategoryId(int categoryId)
{
try
{
var searchResults = _searchClient
.Search<ProductCatalogBlock>()
.Filter(x => x.CategoryId.Match(categoryId))
.Filter(x => x.IsActive.Match(true))
.GetResult();
return searchResults?.ToList() ?? new List<ProductCatalogBlock>();
}
catch (Exception)
{
return new List<ProductCatalogBlock>();
}
}
Giải pháp dựa trên Database (Tối ưu)
Sử dụng IContentLoader với kỹ thuật batch loading để giảm thiểu số lần truy vấn SQL:
public List<ProductCatalogBlock> GetProductsFromCategoryPage(ContentReference categoryPageRef)
{
if (ContentReference.IsNullOrEmpty(categoryPageRef)) return new List<ProductCatalogBlock>();
var categoryPage = _contentLoader.Get<CategoryPage>(categoryPageRef);
// Batch load: Lấy tất cả items trong Content Area trong một lần gọi
var contentLinks = categoryPage.ProductsContentArea.Items
.Select(i => i.ContentLink)
.ToList();
var allContent = _contentLoader.GetItems(contentLinks, CultureInfo.CurrentUICulture);
return allContent.OfType<ProductCatalogBlock>()
.Where(x => x.IsActive)
.ToList();
}
Phân tích Hiệu năng (Benchmarking)
Kết quả benchmark trên dữ liệu thực tế cho thấy kết quả bất ngờ:
| Phương pháp | Độ trễ TB | P95 Latency | Độ phức tạp |
|---|---|---|---|
| Dựa trên Find | ~150ms | ~250ms | Cao (Cloud IO) |
| Dựa trên Database | ~20ms | ~35ms | Thấp (In-process) |
Phân tích Chi tiết
- Find Approach: Tốt cho tập dữ liệu lớn (1000+ items). Tuy nhiên, với 25 items, chi phí kết nối mạng tới Find Cloud tạo ra độ trễ lớn hơn nhiều so với truy vấn tại chỗ.
- Database Approach: Cực kỳ nhanh cho tập dữ liệu nhỏ nhờ cơ chế Level-2 Cache nội bộ của Optimizely. Dữ liệu luôn là thời gian thực, không có độ trễ indexing.
Nguyên tắc Kiến trúc: Chọn đúng công cụ
Anti-Pattern: Tối ưu hóa sớm (Premature Optimization)
Việc ép buộc sử dụng Find cho 25 items là một ví dụ điển hình của tối ưu hóa sớm. Nó làm tăng độ phức tạp và phụ thuộc vào dịch vụ bên ngoài mà không mang lại lợi ích thực tế về hiệu năng.
Mô hình Kiến trúc Hybrid
Một kiến trúc tốt nên linh hoạt theo khối lượng dữ liệu:
public class ProductCatalogService : IProductCatalogService
{
private const int FIND_THRESHOLD = 100; // Ngưỡng chuyển đổi
public List<ProductCatalogBlock> GetProducts(int categoryId)
{
int estimatedCount = GetEstimateCount(categoryId);
return estimatedCount > FIND_THRESHOLD
? GetFromFind(categoryId)
: GetFromDatabase(categoryId);
}
}
Chiến lược Caching
Để tối ưu hóa phương pháp Database, hãy sử dụng ISynchronizedObjectInstanceCache. Đây là bộ nhớ đệm có khả năng đồng bộ giữa các node trong cụm load-balancing.
private void OnContentPublished(object sender, ContentEventArgs e)
{
if (e.Content is ProductCatalogBlock block)
{
// Tự động xóa cache khi dữ liệu thay đổi
_cache.Remove($"ProductCatalog_{block.CategoryId}");
}
}
Kết luận
Trong trường hợp này, việc quay lại sử dụng database cho các tập dữ liệu nhỏ là quyết định đúng đắn. Find là công cụ mạnh mẽ, nhưng chỉ nên dùng khi bài toán yêu cầu tìm kiếm quy mô lớn, lọc phức tạp hoặc phân trang sâu.
Lời khuyên: Hãy bắt đầu đơn giản với IContentLoader và caching thông minh. Chỉ chuyển sang Find khi các chỉ số hiệu năng thực tế chứng minh sự cần thiết.