Tôi đã tìm kiếm rất nhiều vấn đề về hiệu năng và thử nhiều thứ khác nhau, nhưng tôi không thể làm cho nó hoạt động đủ nhanh. Đây là vấn đề của tôi với hình thức đơn giản nhất:CodeFirst loading 1 parent được liên kết với 25 000 trẻ em chậm
Tôi đang sử dụng khung thực thể 5 và tôi muốn có thể tải các phiên bản con lười của cha mẹ khi người dùng chọn phụ huynh đó, vì vậy tôi không phải kéo toàn bộ cơ sở dữ liệu. Tuy nhiên tôi đã gặp vấn đề về hiệu suất với việc tải trẻ em xuống. Tôi nghĩ rằng vấn đề là dây lên của các tài sản chuyển hướng giữa phụ huynh và trẻ em. Tôi cũng nghĩ rằng nó phải là một cái gì đó tôi đã làm sai vì tôi tin rằng đây là một trường hợp đơn giản.
Vì vậy, tôi đã thiết lập một chương trình để kiểm tra một tải đơn lẻ để cô lập sự cố.
Đây là bài kiểm tra:
Tôi đã tạo một lớp học POCO dành cho cha mẹ và một Lớp học trẻ em POCO. Phụ huynh có n Trẻ em và Trẻ em có 1 Phụ huynh. Chỉ có 1 phụ huynh trong cơ sở dữ liệu SQL Server và 25 000 trẻ em cho cha mẹ đơn đó. Tôi đã thử các phương pháp khác nhau để tải dữ liệu này. Bất cứ khi nào tôi tải một trong hai trẻ em và phụ huynh trong cùng một DbContext, phải mất một thời gian rất dài. Nhưng nếu tôi tải chúng trong DbContexts khác nhau, nó tải rất nhanh. Tuy nhiên, tôi muốn những trường hợp đó trong cùng một DbContext.
Đây là thiết lập thử nghiệm của tôi và tất cả mọi thứ bạn cần để tái tạo nó:
POCOs:
public class Parent
{
public int ParentId { get; set; }
public string Name { get; set; }
public virtual List<Child> Childs { get; set; }
}
public class Child
{
public int ChildId { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
DbContext:
public class Entities : DbContext
{
public DbSet<Parent> Parents { get; set; }
public DbSet<Child> Childs { get; set; }
}
TSQL Script để tạo ra the Datab ase và dữ liệu:
USE [master]
GO
IF EXISTS(SELECT name FROM sys.databases
WHERE name = 'PerformanceParentChild')
alter database [PerformanceParentChild] set single_user with rollback immediate
DROP DATABASE [PerformanceParentChild]
GO
CREATE DATABASE [PerformanceParentChild]
GO
USE [PerformanceParentChild]
GO
BEGIN TRAN T1;
SET NOCOUNT ON
CREATE TABLE [dbo].[Parents]
(
[ParentId] [int] CONSTRAINT PK_Parents PRIMARY KEY,
[Name] [nvarchar](200) NULL
)
GO
CREATE TABLE [dbo].[Children]
(
[ChildId] [int] CONSTRAINT PK_Children PRIMARY KEY,
[ParentId] [int] NOT NULL,
[Name] [nvarchar](200) NULL
)
GO
INSERT INTO Parents (ParentId, Name)
VALUES (1, 'Parent')
DECLARE @nbChildren int;
DECLARE @childId int;
SET @nbChildren = 25000;
SET @childId = 0;
WHILE @childId < @nbChildren
BEGIN
SET @childId = @childId + 1;
INSERT INTO [dbo].[Children] (ChildId, ParentId, Name)
VALUES (@childId, 1, 'Child #' + convert(nvarchar(5), @childId))
END
CREATE NONCLUSTERED INDEX [IX_ParentId] ON [dbo].[Children]
(
[ParentId] ASC
)
GO
ALTER TABLE [dbo].[Children] ADD CONSTRAINT [FK_Children.Parents_ParentId] FOREIGN KEY([ParentId])
REFERENCES [dbo].[Parents] ([ParentId])
GO
COMMIT TRAN T1;
App.cấu hình có chứa các chuỗi kết nối:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<connectionStrings>
<add
name="Entities"
providerName="System.Data.SqlClient"
connectionString="Server=localhost;Database=PerformanceParentChild;Trusted_Connection=true;"/>
</connectionStrings>
</configuration>
thử nghiệm giao diện điều khiển lớp:
class Program
{
static void Main(string[] args)
{
List<Parent> parents;
List<Child> children;
Entities entities;
DateTime before;
TimeSpan childrenLoadElapsed;
TimeSpan parentLoadElapsed;
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load only the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
children = entities.Childs.ToList();
childrenLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load only the children from DbSet:" + childrenLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
before = DateTime.Now;
children = entities.Childs.ToList();
childrenLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds" +
", then load the children from DbSet:" + childrenLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
children = entities.Childs.ToList();
childrenLoadElapsed = DateTime.Now - before;
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the children from DbSet:" + childrenLoadElapsed.TotalSeconds + " seconds" +
", then load the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.ToList();
parentLoadElapsed = DateTime.Now - before;
before = DateTime.Now;
children = parents[0].Childs;
childrenLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet:" + parentLoadElapsed.TotalSeconds + " seconds" +
", then load the children from Parent's lazy loaded navigation property:" + childrenLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
before = DateTime.Now;
parents = entities.Parents.Include(p => p.Childs).ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet and children from include:" + parentLoadElapsed.TotalSeconds + " seconds");
}
using (entities = new Entities())
{
entities.Configuration.ProxyCreationEnabled = false;
entities.Configuration.AutoDetectChangesEnabled = false;
entities.Configuration.LazyLoadingEnabled = false;
entities.Configuration.ValidateOnSaveEnabled = false;
before = DateTime.Now;
parents = entities.Parents.Include(p => p.Childs).ToList();
parentLoadElapsed = DateTime.Now - before;
System.Diagnostics.Debug.WriteLine("Load the parent from DbSet and children from include:" + parentLoadElapsed.TotalSeconds + " seconds with everything turned off");
}
}
}
Dưới đây là kết quả của những thử nghiệm:
tải chỉ phụ huynh từ DbSet: 0.972 giây
Chỉ tải trẻ em từ DbSet: 0,714 giây onds
tải phụ huynh từ DbSet: 0.001 giây, sau đó tải các trẻ em từ DbSet: 8,6026 giây
tải những đứa trẻ từ DbSet: 0,6864 giây, sau đó tải phụ huynh từ DbSet: 7,5816159 giây
tải phụ huynh từ DbSet: 0 giây, sau đó tải các trẻ em từ bất động sản chuyển hướng lười nạp của phụ huynh: 8,5644549 giây
tải phụ huynh từ DbSet và trẻ em từ bao gồm: 8,6428788 giây
Loa d phụ huynh từ DbSet và trẻ em từ bao gồm: 9,1416586 seconds với tất cả mọi thứ tắt
Phân tích
Bất cứ khi nào phụ huynh và các em đang trong DbContext cùng, phải mất một thời gian dài (9 giây) để kết nối mọi thứ. Tôi thậm chí đã cố gắng tắt tất cả mọi thứ từ tạo proxy để tải chậm, nhưng vô ích. Ai đó có thể vui lòng giúp tôi ?
+1: Câu hỏi và phân tích tuyệt vời! – Slauma