Thursday, May 14, 2026
GitHubTwitter
GINBOK
Trang chủBài viếtTìm kiếmVề chúng tôi
|ENVIKhám phá
Trang chủBài viếtTìm kiếmVề chúng tôi
🇬🇧 English🇻🇳 Tiếng Việt
Bài viết›Engineering Notes›Tối ưu N+1 Query trong Optimizely CMS: Case Study Thực Tế
Engineering Notes

Tối ưu N+1 Query trong Optimizely CMS: Case Study Thực Tế

GinbokMar 1, 20262 phút đọc

Chi phí tiềm ẩn của các ContentArea lồng nhau

Trong Optimizely CMS, tính linh hoạt của ContentArea cho phép các biên tập viên tạo ra cấu trúc nội dung lồng nhau phức tạp. Tuy nhiên, điều này thường dẫn đến vấn đề N+1 query. Chúng tôi đã xử lý một trang Store nơi việc tải một trang duy nhất thực hiện tới ~181 truy vấn database. Nguyên nhân là do vòng lặp lồng nhau gọi _contentLoader.Get<T>() cho từng item riêng lẻ.

Anti-Pattern: Tải dữ liệu đơn lẻ

Mã nguồn ban đầu tải nội dung từng bước một trong các vòng lặp lồng nhau. Điều này tạo ra nút thắt cổ chai về hiệu năng vì mỗi lần gọi hàm là một lần truy vấn vào cơ sở dữ liệu.

// ❌ Anti-pattern gây ra N+1
foreach (var item in storePage.ContentArea.Items)
{
    var content = _contentLoader.Get<IContent>(item.ContentLink); 
    if (content is LayoutRowBlock row)
    {
        foreach (var rowItem in row.ContentArea.Items)
        {
            var child = _contentLoader.Get<IContent>(rowItem.ContentLink); // ❌ Truy vấn cho mỗi con
        }
    }
}

Giải pháp: Tải theo lô với GetItems()

Chiến lược để loại bỏ N+1 rất đơn giản: Thu thập tất cả reference trước, tải một lần duy nhất, sau đó xử lý trong bộ nhớ. Hàm IContentLoader.GetItems() của Optimizely được thiết kế chính xác cho mục đích này.

Triển khai tối ưu

// ✅ 1. Thu thập tất cả links
var contentLinks = storePage.ContentArea.Items
    .Where(i => i?.ContentLink != null)
    .Select(i => i.ContentLink)
    .ToList();

// ✅ 2. Truy vấn batch duy nhất
var allContent = _contentLoader.GetItems(contentLinks, CultureInfo.CurrentUICulture);

// ✅ 3. Lặp trong bộ nhớ
foreach (var content in allContent)
{
    if (content is LayoutRowBlock row)
    {
        var childLinks = row.ContentArea.Items.Select(x => x.ContentLink);
        var rowChildren = _contentLoader.GetItems(childLinks, CultureInfo.CurrentUICulture);
        // Xử lý dữ liệu...
    }
}

Kết quả đạt được

Việc chuyển sang xử lý theo lô đã mang lại những cải thiện rõ rệt:

  • Truy vấn Database: Giảm từ ~181 xuống còn ~11.
  • Thời gian phản hồi: Giảm từ hơn 800ms xuống dưới 150ms.
  • Khả năng mở rộng: Hiệu năng trang không còn giảm dần khi số lượng sản phẩm tăng lên.

Bài học kinh nghiệm

Hãy luôn kiểm tra hiệu năng (profile) trang CMS của bạn. Nếu bạn thấy các câu lệnh SELECT lặp lại liên tục vào bảng tblContent, đó chính là dấu hiệu của N+1. Hãy nhớ: dùng Get<T> cho một item và GetItems cho một danh sách.

#optimizely#backend#performance
← Quay lại bài viết
GINBOK

Deep technical writing for developers and designers who care about the craft.

Content
  • All Articles
  • Engineering
  • Design
  • Product
Company
  • About Ginbok
  • Authors
  • Write for Us
  • Contact
Stay Updated
© 2026 Ginbok. All rights reserved.
PrivacyTerms