Tối ưu hiệu năng với Split Query trong EF Core

Công nghệ - 16/05/2025 01:45:03

🚀 Dùng .Include() trong EF Core liệu có đang âm thầm làm chậm hệ thống của bạn? Bài viết này sẽ hé lộ sự thật về Split Query vs Single Query qua các benchmark thực tế và mẹo tối ưu hiệu quả – đọc để khám phá ngay!

Xin chào các bạn, hôm nay, chúng ta sẽ cùng khám phá một tính năng mạnh mẽ của Entity Framework Core (EF Core): Split Query. Mình sẽ phân tích chi tiết cách Split Query hoạt động, so sánh tốc độ và hiệu năng với Single Query(truy vấn thường), đồng thời đưa ra các ví dụ thực tế và khuyến nghị để bạn áp dụng hiệu quả trong dự án.

Nếu bạn đang đau đầu với hiệu năng truy vấn dữ liệu liên kết, hi vọng bài viết này sẽ giúp bạn tìm ra giải pháp tối ưu hơn.


Split Query và Single Query: Chúng là gì?

Trước khi đi vào so sánh, hãy làm rõ hai khái niệm này:

  • Single Query (tạm gọi là Truy vấn đơn): Đây là cách mặc định của EF Core khi tải dữ liệu liên kết (related data) bằng các method như Include hoặc ThenInclude. EF Core tạo một câu lệnh SQL duy nhất với các lệnh JOIN để lấy toàn bộ dữ liệu từ các bảng liên quan. Ví dụ, nếu bạn truy vấn danh sách đơn hàng (Orders) kèm chi tiết đơn hàng (OrderDetails), EF Core sẽ dùng một truy vấn SQL với LEFT JOIN hoặc INNER JOIN.
  • Split Query (Truy vấn chia nhỏ): Được giới thiệu từ EF Core 5.0, Split Query chia truy vấn thành nhiều câu SQL nhỏ hơn, mỗi câu xử lý một phần dữ liệu (ví dụ: một câu cho bảng Orders, một câu cho bảng OrderDetails). Điều này giúp giảm lượng dữ liệu truyền tải và tránh các vấn đề hiệu năng khi làm việc với dữ liệu lớn hoặc nhiều mối quan hệ.

So sánh hiệu năng: Split Query vs Single Query

Để đánh giá tốc độ và hiệu năng, mình đã tham khảo các benchmark từ tài liệu chính thức của Microsoft và các bài phân tích từ cộng đồng lập trình viên. Dưới đây là những so sánh chi tiết qua các kịch bản thực tế.

1. Kịch bản 1: Dữ liệu nhỏ, ít mối quan hệ

Khi truy vấn một bảng với ít dữ liệu liên kết (ví dụ: danh sách công ty không kèm sản phẩm), Single Query thường nhanh hơn do chỉ cần một lần giao tiếp với database (round-trip).

Benchmark (theo Code Maze):

  • Single Query: 16.64 ms, tiêu thụ 4.04 MB bộ nhớ.
  • Split Query: 18.97 ms, tiêu thụ 4.25 MB bộ nhớ.

Phân tích:

  • Single Query nhanh hơn một chút (khoảng 2 ms) vì chỉ cần một truy vấn SQL, giảm độ trễ mạng.
  • Sự khác biệt về bộ nhớ không đáng kể (4.04 MB so với 4.25 MB).

Kết luận: Với dữ liệu nhỏ và ít mối quan hệ, Single Query là lựa chọn tốt nhờ đơn giản và nhanh chóng.

2. Kịch bản 2: Dữ liệu lớn, nhiều mối quan hệ

Khi truy vấn dữ liệu với nhiều mối quan hệ "một-nhiều" (ví dụ: danh sách công ty kèm sản phẩm), Split Query vượt trội nhờ tránh hiện tượng Cartesian Explosion – khi số hàng trả về tăng đột biến do các lệnh JOIN.

Benchmark (theo Code Maze):

  • Single Query: 200.60 ms, tiêu thụ 46.93 MB bộ nhớ.
  • Split Query: 35.62 ms, tiêu thụ 8.35 MB bộ nhớ.

Phân tích:

  • Split Query nhanh hơn gấp 5-6 lần (35.62 ms so với 200.60 ms) vì nó chia nhỏ truy vấn, tránh việc lặp lại dữ liệu dư thừa từ các bảng liên kết.
  • Tiêu thụ bộ nhớ của Split Query thấp hơn đáng kể (8.35 MB so với 46.93 MB), giúp giảm áp lực lên hệ thống.

Kết luận: Với dữ liệu lớn và nhiều mối quan hệ, Split Query là lựa chọn tối ưu để cải thiện tốc độ và tiết kiệm tài nguyên.

3. Kịch bản 3: Cập nhật dữ liệu

Một bài viết trên Medium đã kiểm tra hiệu năng khi xử lý 7300 sản phẩm với các kịch bản cập nhật:

Cập nhật 3500 sản phẩm:

  • Split Query (lọc trong RAM): 3.15 giây.
  • Single Query (lọc trong RAM): 4.2 giây.

Cập nhật 10 sản phẩm:

  • Split Query (chỉ đọc để cập nhật): 1.20 giây.
  • Single Query (chỉ đọc để cập nhật): 0.6 giây.

Phân tích:

  • Split Query hiệu quả hơn khi đọc và lọc dữ liệu lớn trong RAM (3.15 giây so với 4.2 giây).
  • Single Query nhanh hơn trong các trường hợp cập nhật nhỏ lẻ (0.6 giây so với 1.20 giây) vì giảm số lần round-trip.

Kết luận: Split Query phù hợp cho các tác vụ xử lý dữ liệu lớn, trong khi Single Query tốt hơn cho các cập nhật nhỏ.

4. Phân tích từ Microsoft

Theo tài liệu trên Microsoft Learn, Single Query có thể gây ra hiệu năng kém khi dữ liệu lớn do Cartesian Explosion. Split Query, mặc dù tăng số lần round-trip, lại giảm đáng kể lượng dữ liệu truyền tải, giúp tiết kiệm băng thông và bộ nhớ.


Ví dụ thực tế: Truy vấn đơn hàng và chi tiết

Hãy xem một ví dụ cụ thể để minh họa sự khác biệt giữa hai phương pháp.

Với Single Query

var orders = dbContext.Orders
    .Include(o => o.OrderDetails)
    .ThenInclude(od => od.Product)
    .ToList();

SQL được tạo (giả định):

SELECT o.*, od.*, p.*
    FROM Orders o
    LEFT JOIN OrderDetails od ON o.Id = od.OrderId
    LEFT JOIN Products p ON od.ProductId = p.Id

Vấn đề tiềm ẩn: Nếu một đơn hàng có 10 chi tiết và mỗi chi tiết liên kết với một sản phẩm, số hàng trả về có thể rất lớn, dẫn đến tiêu tốn bộ nhớ và thời gian xử lý.

Với Split Query

var orders = dbContext.Orders

    .Include(o => o.OrderDetails)

    .ThenInclude(od => od.Product)

    .AsSplitQuery()

    .ToList();

SQL được tạo (giả định):

 -- Truy vấn 1: Lấy Orders
SELECT * FROM Orders

-- Truy vấn 2: Lấy OrderDetails
SELECT * FROM OrderDetails WHERE OrderId IN (...)

-- Truy vấn 3: Lấy Products
SELECT * FROM Products WHERE Id IN (...)

Lợi ích: Mỗi truy vấn chỉ lấy dữ liệu cần thiết, giảm số hàng trả về và tránh lặp lại dữ liệu dư thừa.


Khi nào nên dùng Single Query hay Split Query?

Dựa trên các phân tích và kinh nghiệm thực tế, mình đưa ra các khuyến nghị sau:

Sử dụng Single Query khi:

  • Dữ liệu nhỏ, ít mối quan hệ (ví dụ: truy vấn danh sách khách hàng không kèm đơn hàng).
  • Cần đảm bảo tính nhất quán dữ liệu, vì tất cả dữ liệu được lấy trong một lần truy vấn.
  • Độ trễ mạng là vấn đề (database nằm xa, mỗi round-trip tốn thời gian).

Sử dụng Split Query khi:

  • Làm việc với dữ liệu lớn, nhiều mối quan hệ "một-nhiều" (ví dụ: danh sách đơn hàng kèm chi tiết và sản phẩm).
  • Muốn tiết kiệm bộ nhớ và giảm lượng dữ liệu truyền tải.
  • Tính nhất quán dữ liệu không quá quan trọng, hoặc có thể dùng transaction để kiểm soát.

Phương án triển khai Split Query trong EF Core

1. Áp dụng cho từng truy vấn

Sử dụng phương thức AsSplitQuery() để bật Split Query cho một truy vấn cụ thể:

var orders = dbContext.Orders
    .Include(o => o.OrderDetails)
    .ThenInclude(od => od.Product)
    .AsSplitQuery()
    .ToList();

2. Cấu hình toàn cục

Nếu muốn áp dụng Split Query cho tất cả truy vấn, cấu hình trong DbContext:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(connectionString, 
        o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery));
}

3. Single Query khi cần

Nếu đã bật Split Query toàn cục, bạn có thể dùng AsSingleQuery() cho một truy vấn cụ thể:

var orders = dbContext.Orders
    .Include(o => o.OrderDetails)
    .AsSingleQuery()
    .ToList();

Thực hành tốt nhất và lưu ý

  • Tránh Cartesian Explosion với Single Query: Luôn kiểm tra số lượng dữ liệu trả về khi dùng Single Query. Nếu thấy hiệu năng giảm, hãy thử chuyển sang Split Query.
  • Đánh giá độ trễ mạng với Split Query: Split Query tăng số lần round-trip, có thể gây chậm nếu database nằm xa. Hãy đo lường hiệu năng bằng các công cụ như SQL Server Profiler.
  • Dùng transaction khi cần nhất quán dữ liệu: Với Split Query, nếu dữ liệu thay đổi giữa các truy vấn, bạn có thể gặp vấn đề nhất quán. Dùng transaction để kiểm soát, nhưng cẩn thận với khóa bảng hoặc deadlock.
  • Benchmark trong môi trường thực tế: Hiệu năng phụ thuộc vào schema database, lượng dữ liệu, và cấu hình hệ thống. Sử dụng BenchmarkDotNet hoặc MiniProfiler để đo lường chính xác.

Kết hợp với các kỹ thuật tối ưu khác:

  • Dùng AsNoTracking() cho các truy vấn chỉ đọc để giảm overhead.
  • Xem xét phân trang (Skip/Take) để giới hạn dữ liệu trả về.

Kết luận

Split Query Single Query là hai công cụ mạnh mẽ trong Entity Framework Core, mỗi cái phù hợp với các tình huống khác nhau. Split Query tỏ ra vượt trội khi xử lý dữ liệu lớn với nhiều mối quan hệ, giúp giảm thời gian thực thi (nhanh gấp 5-6 lần trong một số trường hợp) và tiết kiệm bộ nhớ. Ngược lại, Single Query phù hợp hơn với dữ liệu nhỏ, ít mối quan hệ, hoặc khi cần giảm độ trễ mạng.

Là một senior .NET developer, mình khuyên bạn nên thử nghiệm cả hai phương pháp trong dự án thực tế, sử dụng các công cụ đo lường để so sánh hiệu năng, và chọn giải pháp phù hợp với yêu cầu cụ thể.

Nếu bạn có mẹo hay kinh nghiệm khi dùng Entity Framework Core, hãy chia sẻ ở phần bình luận để chúng ta cùng học hỏi nhé! Cảm ơn các bạn đã đọc bài viết. Hãy theo dõi blog của mình để cập nhật thêm nhiều bài viết công nghệ tâm huyết khác!

 

Tài liệu tham khảo:

 

 

/Son Do - I share real-world lessons, team building & developer growth.

 

#1percentbetter #EntityFrameworkCore #SplitQuery #SingleQuery #DotNet #NETDevelopment #PerformanceOptimization #DatabaseQuery #CSharp #SQLPerformance #TechBlog #SoftwareEngineering #BackendDevelopment

Công nghệ - 19/08/2025 21:13:07

Tìm hiểu cách xây dựng hệ thống phát hiện ngôn ngữ ký hiệu theo thời gian thực bằng AI, sử dụng DETR để tăng cường khả năng tiếp cận và đổi mới. Kết nối lời nói và cử chỉ.

Công nghệ - 18/08/2025 13:38:25

Tối ưu hóa các hệ thống RAG bằng cách tận dụng siêu dữ liệu để truy xuất thông tin chính xác và nhanh chóng hơn, giải quyết các thách thức về dữ liệu dư thừa hoặc lỗi thời với công cụ LangExtract nguồn mở. Khám phá cách LangExtract sử dụng các mô hình ngôn ngữ tiên tiến để trích xuất và cấu trúc siêu dữ liệu, tạo ra một quy trình truy xuất hợp lý và hiệu quả.

Công nghệ - 01/08/2025 07:00:00

Gỡ lỗi LLM rất quan trọng vì quy trình làm việc của chúng phức tạp và liên quan đến nhiều phần như chuỗi, lời nhắc, API, công cụ, trình truy xuất, v.v.

Công nghệ - 19/06/2025 03:05:09

Code xong chạy được là chưa đủ – phải biết khi nào nó "chết" nữa chứ 😅

Bạn đang triển khai ứng dụng trên Kubernetes, Docker hay môi trường production nào? Và bạn từng "toát mồ hôi" vì service chết mà không ai báo?

Công nghệ - 16/07/2025 13:41:17

Bạn có bao giờ tự hỏi tại sao trang web của mình tải chậm, đặc biệt là trên các thiết bị di động? Rất có thể, thủ phạm chính là những hình ảnh chưa được tối ưu. May mắn thay, có một công cụ miễn phí và cực kỳ hữu ích có thể giúp bạn giải quyết vấn đề này: Responsive Image Linter – một tiện ích mở rộng trên Chrome. Video này sẽ giới thiệu chi tiết về công cụ này, giúp bạn xác định và tối ưu hóa các hình ảnh gây tốn hiệu năng trên trang web của mình.

Công nghệ - 27/06/2025 03:15:44

⏳ Chậm 3 giây – Mất 50% người dùng. Đó không còn là lý thuyết, đó là thực tế.

Công nghệ - 11/12/2025 15:05:29

[Góc chuyện nghề] bán account game để đi học nghệ - bạn dám không?

Làm nghề 20 năm, gặp nhiều sinh viên, nhưng chiều qua tôi khá bất ngờ với một cậu em tên Quang. Em Quang muốn theo nghề BA và mong muốn lương 20 triệu sau khi làm việc 1.5 năm tới 2 năm trong nghề.

Công nghệ - 22/09/2025 08:59:20

Dừng ngay việc dùng DateTime.Now trong APIs, đó là ổ lỗi tiềm ẩn trong hệ thống của bạn

⏱️ Tôi từng nghĩ DateTime.Now là một thứ vô hại, đơn giản và tiện lợi, cho đến khi gặp những vấn đề về múi giờ. Những lỗi "tưởng chừng nhỏ" này lại chính là nguồn cơn của sự thất vọng và tốn kém thời gian cho nhiều đội ngũ phát triển.

Công nghệ - 14/03/2025 04:30:32

💡Bạn muốn tăng tốc tìm kiếm toàn văn nhưng hạ tầng hạn chế? Lucene có thể là giải pháp bất ngờ! Bài viết tiết lộ cách nó vượt trội hơn SQL Server, tối ưu truy vấn và những ứng dụng thực tế đáng khám phá.