Entity Framework có phù hợp với các trang web có lưu lượng truy cập cao không?


176

Entity Framework 4 có phải là giải pháp tốt cho một trang web công cộng có khả năng 1000 lượt truy cập / giây không?

Theo hiểu biết của tôi, EF là một giải pháp khả thi cho hầu hết các trang web nhỏ hơn hoặc mạng nội bộ, nhưng sẽ không dễ dàng mở rộng quy mô cho một trang web cộng đồng phổ biến (tôi biết SO đang sử dụng LINQ to SQL, nhưng .. Tôi muốn có nhiều ví dụ / bằng chứng hơn. ..)

Bây giờ tôi đang đứng ở ngã tư đường hoặc chọn cách tiếp cận ADO.NET thuần túy hoặc EF4. Bạn có nghĩ rằng năng suất của nhà phát triển được cải thiện với EF đáng để mất hiệu năng và quyền truy cập chi tiết của ADO.NET (với các thủ tục được lưu trữ) không? Bất kỳ vấn đề nghiêm trọng nào mà một trang web có lưu lượng truy cập cao có thể gặp phải, đó có phải là sử dụng EF không?

Cảm ơn bạn trước.


1
Bạn không hiểu tỉ lệ. Chia tỷ lệ có nghĩa là nhận được thông lượng gấp 10 lần khi bạn thêm dung lượng 10 lần. Tại sao EF ngăn chặn điều này xảy ra? Nó thêm một yếu tố chi phí không đổi cho bất kỳ khối lượng công việc cơ sở dữ liệu.
usr

Câu trả lời:


152

Nó phụ thuộc một chút vào mức độ trừu tượng mà bạn cần . Mọi thứ đều là sự thỏa hiệp; ví dụ, EF và NHibernate giới thiệu tính linh hoạt cao để thể hiện dữ liệu theo các mô hình thú vị và kỳ lạ - nhưng kết quả là chúng thêm chi phí hoạt động. Chi phí đáng chú ý.

Nếu bạn không cần phải có khả năng chuyển đổi giữa các nhà cung cấp cơ sở dữ liệu và các bố trí bảng trên mỗi khách hàng khác nhau và nếu dữ liệu của bạn chủ yếu được đọc và nếu bạn không cần phải sử dụng cùng một mô hình trong EF, SSRS , Dịch vụ dữ liệu ADO.NET, v.v. - sau đó nếu bạn muốn hiệu suất tuyệt đối làm thước đo chính của mình, bạn có thể làm điều tồi tệ hơn nhiều so với nhìn vào dapper . Trong các thử nghiệm của chúng tôi dựa trên cả LINQ-to-SQL và EF, chúng tôi thấy rằng EF chậm hơn đáng kể về hiệu suất đọc thô, có lẽ là do các lớp trừu tượng (giữa mô hình lưu trữ, v.v.) và vật chất hóa.

Tại SO, chúng tôi bị ám ảnh về hiệu suất thô và chúng tôi rất vui khi nhận được sự phát triển của việc mất một số trừu tượng để đạt được tốc độ. Như vậy, công cụ chính của chúng tôi để truy vấn cơ sở dữ liệu là dapper . Điều này thậm chí cho phép chúng ta sử dụng mô hình LINQ-to-SQL đã có từ trước, nhưng đơn giản: nó là đống nhanh hơn. Trong các thử nghiệm hiệu năng, về cơ bản chính xác là hiệu suất tương tự như viết tất cả mã ADO.NET (tham số, trình đọc dữ liệu, v.v.) theo cách thủ công, nhưng không có nguy cơ bị nhầm tên cột. Tuy nhiên, đó là dựa trên SQL (mặc dù rất vui khi sử dụng XUÂN nếu đó là chất độc bạn chọn). Ưu điểm của việc này là không có xử lý bổ sung liên quan, nhưng nó một hệ thống dành cho những người thích SQL. Mà tôi xem xét: không phải là một điều xấu!

Một truy vấn điển hình, ví dụ, có thể là:

int customerId = ...
var orders = connection.Query<Order>(
    "select * from Orders where CustomerId = @customerId ",
    new { customerId }).ToList();

đó là thuận tiện, an toàn tiêm, vv - nhưng không có hàng tấn người đọc dữ liệu. Lưu ý rằng mặc dù nó có thể xử lý cả phân vùng ngang và dọc để tải các cấu trúc phức tạp, nhưng nó sẽ không hỗ trợ tải lười biếng (nhưng: chúng tôi là những người hâm mộ lớn của việc tải rất rõ ràng - ít bất ngờ hơn).

Lưu ý trong câu trả lời này Tôi không nói rằng EF không phù hợp với công việc có khối lượng lớn; chỉ đơn giản là: tôi biết rằng dapper là tùy thuộc vào nó.


25
+1 cho dapper. Sử dụng một ORM phức tạp cho các mô hình đọc là không cần thiết. Cách tiếp cận chúng tôi thực hiện bây giờ là sử dụng ORM cho mô hình miền của chúng tôi (trong đó công cụ ORM ưa thích thực sự hữu ích) và dapper cho mô hình đọc của chúng tôi. Điều này làm cho các ứng dụng siêu nhanh.

2
@Marc, cảm ơn bạn vì câu trả lời tuyệt vời - Cuối cùng tôi cũng có thể đưa ra quyết định của mình một cách tự tin! Chắc chắn sẽ xem xét chi tiết hơn sau này. Thực sự thích nó như thế nào chỉ là một tập tin :)

3
Tôi đã viết lên ORM của riêng tôi. Nó chậm. Tôi nhìn dapper và thích nó. Bây giờ tôi sử dụng dapper cho tất cả các lần đọc và ORM của riêng tôi để chèn (hỗ trợ FK, giao dịch và tất cả nội dung tốt). Đây là mã dễ đọc nhất tôi từng viết.

2
@ acidzombie24 dapper hỗ trợ các giao dịch và phần đóng góp của dapper (không phải là một phần của việc triển khai nuget) đang đạt được các tùy chọn chèn vv. Chỉ cần đề cập cho đầy đủ. Tôi rất vui vì dapper rất tiện dụng.
Marc Gravell

1
@anyname Tôi chưa bao giờ thực hiện một khóa học video về bất kỳ chủ đề nào; có một số video trên mạng, nhưng không phải bởi tôi. Tôi có xu hướng trở thành một người viết chữ
Marc Gravell

217

Câu hỏi "Tôi nên sử dụng ORM nào" thực sự nhắm vào phần nổi của tảng băng khổng lồ khi nói đến chiến lược truy cập dữ liệu tổng thể và tối ưu hóa hiệu suất trong một ứng dụng quy mô lớn.

Tất cả những điều sau đây ( gần như theo thứ tự quan trọng) sẽ ảnh hưởng đến thông lượng và tất cả chúng đều được xử lý (đôi khi theo những cách khác nhau) bởi hầu hết các khung ORM chính ngoài kia:

  1. Thiết kế và bảo trì cơ sở dữ liệu

    Điều này là, bởi một biên độ rộng, là yếu tố quyết định quan trọng nhất của thông lượng của một ứng dụng hoặc trang web dựa trên dữ liệu và thường bị các lập trình viên bỏ qua hoàn toàn.

    Nếu bạn không sử dụng các kỹ thuật chuẩn hóa phù hợp, trang web của bạn sẽ bị tiêu diệt. Nếu bạn không có khóa chính, hầu hết mọi truy vấn sẽ chậm. Nếu bạn sử dụng các kiểu chống nổi tiếng như sử dụng bảng cho Cặp giá trị khóa (Thực thể-giá trị thuộc tính AKA) mà không có lý do chính đáng, bạn sẽ làm nổ số lần đọc và ghi vật lý.

    Nếu bạn không tận dụng các tính năng mà cơ sở dữ liệu cung cấp cho bạn, chẳng hạn như nén trang, FILESTREAMlưu trữ (đối với dữ liệu nhị phân), SPARSEcột, hierarchyidcho phân cấp, v.v. (tất cả các ví dụ về SQL Server), thì bạn sẽ không thấy bất cứ nơi nào gần hiệu suất mà bạn có thể được nhìn thấy.

    Bạn nên bắt đầu lo lắng về chiến lược truy cập dữ liệu của mình sau khi bạn đã thiết kế cơ sở dữ liệu của mình và tự thuyết phục bản thân rằng nó tốt nhất có thể, ít nhất là trong thời điểm hiện tại.

  2. Háo hức so với tải nhanh

    Hầu hết các ORM đã sử dụng một kỹ thuật gọi là lười tải cho các mối quan hệ, có nghĩa là theo mặc định, nó sẽ tải một thực thể (hàng bảng) tại một thời điểm và thực hiện một chuyến đi khứ hồi tới cơ sở dữ liệu mỗi khi nó cần tải một hoặc nhiều liên quan (nước ngoài phím) hàng.

    Đây không phải là điều tốt hay xấu, nó phụ thuộc vào những gì thực sự sẽ được thực hiện với dữ liệu và mức độ bạn biết trước. Đôi khi lười tải là hoàn toàn đúng đắn. NHibernate, ví dụ, có thể quyết định không truy vấn bất cứ điều gì cả và chỉ cần tạo proxy cho một ID cụ thể. Nếu tất cả những gì bạn cần là chính ID, tại sao nó phải yêu cầu nhiều hơn? Mặt khác, nếu bạn đang cố in một cây của mỗi phần tử trong hệ thống phân cấp 3 cấp, thì việc tải nhanh trở thành thao tác O (N²), điều này cực kỳ tệ cho hiệu suất.

    Một lợi ích thú vị khi sử dụng "SQL thuần" (nghĩa là các truy vấn / thủ tục lưu trữ ADO.NET thô) là về cơ bản, nó buộc bạn phải suy nghĩ chính xác dữ liệu nào là cần thiết để hiển thị bất kỳ màn hình hoặc trang cụ thể nào. ORMs và các tính năng lười biếng nạp không ngăn cản bạn làm điều này, nhưng họ làm cho bạn cơ hội để được ... tốt, lười biếng , và vô tình phát nổ số truy vấn bạn thực hiện. Vì vậy, bạn cần hiểu các tính năng tải háo hức ORM của mình và luôn cảnh giác về số lượng truy vấn bạn gửi đến máy chủ cho bất kỳ yêu cầu trang nào.

  3. Bộ nhớ đệm

    Tất cả các ORM chính đều duy trì bộ đệm cấp một, "bộ đệm nhận dạng" AKA, có nghĩa là nếu bạn yêu cầu cùng một thực thể hai lần bằng ID của nó, thì nó không yêu cầu chuyến đi khứ hồi thứ hai và cả (nếu bạn thiết kế cơ sở dữ liệu của mình một cách chính xác ) cung cấp cho bạn khả năng sử dụng đồng thời lạc quan.

    Bộ đệm L1 khá mờ trong L2S ​​và EF, bạn phải tin rằng nó hoạt động. NHibernate rõ ràng hơn về nó ( Get/ Loadvs. Query/ QueryOver). Tuy nhiên, miễn là bạn cố gắng truy vấn bằng ID càng nhiều càng tốt, bạn sẽ ổn ở đây. Rất nhiều người quên đi bộ đệm L1 và liên tục tìm kiếm cùng một thực thể nhiều lần bởi một thứ khác không phải ID của nó (tức là trường tra cứu). Nếu bạn cần làm điều này thì bạn nên lưu ID hoặc thậm chí toàn bộ thực thể để tra cứu trong tương lai.

    Ngoài ra còn có bộ đệm cấp 2 ("bộ đệm truy vấn"). NHibernate có tích hợp sẵn này. Linq to SQL và Entity Framework đã biên dịch các truy vấn , có thể giúp giảm tải máy chủ ứng dụng khá nhiều bằng cách tự biên dịch biểu thức truy vấn, nhưng nó không lưu trữ dữ liệu. Microsoft dường như coi đây là mối quan tâm của ứng dụng hơn là mối quan tâm truy cập dữ liệu và đây là điểm yếu lớn của cả L2S và EF. Không cần phải nói đó cũng là một điểm yếu của SQL "thô". Để có được hiệu suất thực sự tốt với cơ bản bất kỳ ORM nào khác ngoài NHibernate, bạn cần phải thực hiện mặt tiền bộ nhớ đệm của riêng mình.

    Ngoài ra còn có một bộ nhớ cache L2 "mở rộng" cho EF4 đó là ổn , nhưng không thực sự là một sự thay thế bán buôn cho một bộ nhớ cache ứng dụng cấp.

  4. Số lượng truy vấn

    Cơ sở dữ liệu quan hệ được dựa trên bộ dữ liệu. Chúng thực sự tốt trong việc tạo ra một lượng lớn dữ liệu trong một khoảng thời gian ngắn, nhưng chúng gần như không tốt về độ trễ truy vấn vì có một lượng chi phí nhất định liên quan đến mỗi lệnh. Một ứng dụng được thiết kế tốt sẽ phát huy những điểm mạnh của DBMS này và cố gắng giảm thiểu số lượng truy vấn và tối đa hóa lượng dữ liệu trong mỗi truy vấn.

    Bây giờ tôi không nói là truy vấn toàn bộ cơ sở dữ liệu khi bạn chỉ cần một hàng. Những gì tôi đang nói là, nếu bạn cần Customer, Address, Phone, CreditCard, và Orderhàng tất cả cùng một lúc nhằm phục vụ một trang duy nhất, sau đó bạn nên hỏi cho họ tất cả cùng một lúc, không thực hiện mỗi truy vấn riêng biệt. Đôi khi điều đó còn tệ hơn thế, bạn sẽ thấy mã truy vấn cùng một Customerbản ghi 5 lần liên tiếp, đầu tiên để lấy Id, sau đó Name, sau đó EmailAddress, sau đó ... nó không hiệu quả một cách lố bịch.

    Ngay cả khi bạn cần thực thi một số truy vấn tất cả hoạt động trên các tập dữ liệu hoàn toàn khác nhau, thì việc gửi tất cả đến cơ sở dữ liệu dưới dạng một "tập lệnh" sẽ vẫn hiệu quả hơn và trả về nhiều tập kết quả. Đó là chi phí bạn quan tâm chứ không phải tổng lượng dữ liệu.

    Điều này nghe có vẻ như lẽ thường nhưng thực sự rất dễ để mất dấu vết của tất cả các truy vấn đang được thực hiện trong các phần khác nhau của ứng dụng; Nhà cung cấp thành viên của bạn truy vấn các bảng người dùng / vai trò, hành động Tiêu đề của bạn truy vấn giỏ hàng, hành động Menu của bạn truy vấn bảng sơ đồ trang web, hành động Sidebar của bạn truy vấn danh sách sản phẩm nổi bật và sau đó có thể trang của bạn được chia thành một vài khu vực tự trị riêng biệt. truy vấn riêng các bảng Lịch sử đơn hàng, Đã xem gần đây, Danh mục và Khoảng không quảng cáo và trước khi bạn biết, bạn đang thực hiện 20 truy vấn trước khi bạn thậm chí có thể bắt đầu phân phát trang. Nó chỉ phá hủy hoàn toàn hiệu suất.

    Một số khung - và tôi nghĩ chủ yếu là NHibernate ở đây - cực kỳ thông minh về điều này và cho phép bạn sử dụng một thứ gọi là tương lai , xử lý toàn bộ các truy vấn và cố gắng thực hiện tất cả chúng cùng một lúc, vào phút cuối có thể. AFAIK, bạn tự mình làm nếu bạn muốn làm điều này với bất kỳ công nghệ nào của Microsoft; bạn phải xây dựng nó thành logic ứng dụng của bạn.

  5. Lập chỉ mục, dự đoán và dự đoán

    Ít nhất 50% các nhà phát triển tôi nói chuyện và thậm chí một số DBA dường như gặp rắc rối với khái niệm bao gồm các chỉ số. Họ nghĩ, "tốt, Customer.Namecột được lập chỉ mục, vì vậy mọi tra cứu tôi làm trên tên phải nhanh." Ngoại trừ nó không hoạt động theo cách đó trừ khi Namechỉ mục bao gồm cột cụ thể mà bạn đang tìm kiếm. Trong SQL Server, điều đó được thực hiện với INCLUDEtrong CREATE INDEXcâu lệnh.

    Nếu bạn vô tình sử dụng SELECT *ở mọi nơi - và đó là nhiều hơn hoặc ít hơn mọi ORM sẽ làm trừ khi bạn chỉ định rõ ràng bằng cách sử dụng phép chiếu - thì DBMS rất có thể chọn bỏ qua hoàn toàn các chỉ mục của bạn vì chúng chứa các cột không được bảo hiểm. Một phép chiếu có nghĩa là, ví dụ, thay vì làm điều này:

    from c in db.Customers where c.Name == "John Doe" select c
    

    Bạn làm điều này thay vào đó:

    from c in db.Customers where c.Name == "John Doe"
    select new { c.Id, c.Name }
    

    Và ý chí này, đối với hầu hết ORMs hiện đại, hướng dẫn nó để chỉ đi và truy vấn IdNamecác cột được có lẽ bao phủ bởi các chỉ số (nhưng không phải là Email, LastActivityDatehoặc bất cứ điều gì khác cột mà bạn tình cờ dính vào trong đó).

    Nó cũng rất dễ dàng để thổi bay hoàn toàn mọi lợi ích lập chỉ mục bằng cách sử dụng các vị từ không phù hợp. Ví dụ:

    from c in db.Customers where c.Name.Contains("Doe")
    

    ... Trông gần giống với truy vấn trước đây của chúng tôi nhưng thực tế sẽ dẫn đến quét toàn bộ bảng hoặc chỉ mục vì nó dịch sang LIKE '%Doe%'. Tương tự, một truy vấn khác có vẻ đơn giản đáng ngờ là:

    from c in db.Customers where (maxDate == null) || (c.BirthDate >= maxDate)
    

    Giả sử bạn có một chỉ mục trên BirthDate, vị từ này có cơ hội tốt để khiến nó hoàn toàn vô dụng. Lập trình viên giả định của chúng tôi ở đây rõ ràng đã cố gắng tạo ra một loại truy vấn động ("chỉ lọc ngày sinh nếu tham số đó được chỉ định"), nhưng đây không phải là cách đúng để làm điều đó. Viết như thế này thay thế:

    from c in db.Customers where c.BirthDate >= (maxDate ?? DateTime.MinValue)
    

    ... bây giờ công cụ DB biết cách tham số hóa điều này và thực hiện tìm kiếm chỉ mục. Một thay đổi nhỏ, dường như không đáng kể đối với biểu thức truy vấn có thể ảnh hưởng mạnh đến hiệu suất.

    Thật không may, LINQ nói chung làm cho tất cả quá dễ dàng để viết các truy vấn xấu như thế này bởi vì đôi khi các nhà cung cấp có thể đoán những gì bạn đang cố gắng làm và tối ưu hóa truy vấn, và đôi khi chúng không. Vì vậy, bạn kết thúc với kết quả không nhất quán một cách bực bội , điều hiển nhiên rõ ràng (với một DBA có kinh nghiệm, dù sao) nếu bạn chỉ viết SQL cũ đơn giản.

    Về cơ bản, tất cả đều xuất phát từ thực tế là bạn thực sự phải theo dõi chặt chẽ cả SQL được tạo và các kế hoạch thực hiện mà chúng dẫn đến, và nếu bạn không nhận được kết quả như mong đợi, đừng ngại bỏ qua Lớp ORM thỉnh thoảng và mã hóa SQL. Điều này áp dụng cho bất kỳ ORM nào , không chỉ riêng EF.

  6. Giao dịch và khóa

    Bạn có cần hiển thị dữ liệu hiện tại lên đến mili giây không? Có lẽ - nó phụ thuộc - nhưng có lẽ là không. Đáng buồn thay, Entity Framework không cung cấp cho bạnnolock , bạn chỉ có thể sử dụng READ UNCOMMITTEDở cấp độ giao dịch (không phải cấp độ bảng). Trong thực tế, không có ORM nào đặc biệt đáng tin cậy về điều này; nếu bạn muốn đọc bẩn, bạn phải thả xuống cấp độ SQL và viết các truy vấn đặc biệt hoặc các thủ tục được lưu trữ. Vì vậy, những gì nó sôi lên, một lần nữa, là nó dễ dàng như thế nào để bạn làm điều đó trong khuôn khổ.

    Entity Framework đã đi một chặng đường dài về vấn đề này - phiên bản 1 của EF (trong .NET 3.5) thật tuyệt vời, khiến cho việc vượt qua sự trừu tượng của các "thực thể" trở nên vô cùng khó khăn, nhưng bây giờ bạn đã có ExecuteStoreQueryDịch , vì vậy nó thực sự không tệ lắm. Kết bạn với những anh chàng này vì bạn sẽ sử dụng họ rất nhiều.

    Ngoài ra còn có vấn đề về khóa ghi và khóa chết và thực tiễn chung là giữ khóa trong cơ sở dữ liệu trong ít thời gian nhất có thể. Về vấn đề này, hầu hết các ORM (bao gồm Khung thực thể) thực sự có xu hướng tốt hơn SQL thô vì chúng gói gọn đơn vị của mẫu Công việc , trong EF là SaveChanges . Nói cách khác, bạn có thể "chèn" hoặc "cập nhật" hoặc "xóa" các thực thể vào nội dung trái tim của mình, bất cứ khi nào bạn muốn, đảm bảo rằng kiến ​​thức sẽ không bị thay đổi thực sự vào cơ sở dữ liệu cho đến khi bạn cam kết đơn vị công việc.

    Lưu ý rằng UOW không giống với giao dịch dài hạn. UOW vẫn sử dụng các tính năng tương tranh lạc quan của ORM và theo dõi tất cả các thay đổi trong bộ nhớ . Không một câu lệnh DML nào được phát ra cho đến khi cam kết cuối cùng. Điều này giữ cho thời gian giao dịch càng thấp càng tốt. Nếu bạn xây dựng ứng dụng của mình bằng SQL thô, sẽ rất khó để đạt được hành vi hoãn lại này.

    Điều này có ý nghĩa gì đối với EF cụ thể: Làm cho các đơn vị công việc của bạn càng thô càng tốt và không cam kết chúng cho đến khi bạn thực sự cần. Làm điều này và bạn sẽ kết thúc với sự tranh chấp khóa thấp hơn nhiều so với việc bạn sử dụng các lệnh ADO.NET riêng lẻ vào các thời điểm ngẫu nhiên.

Túm cái vạy lại là:

EF hoàn toàn tốt cho các ứng dụng có lưu lượng truy cập cao / hiệu suất cao, giống như mọi khung công tác khác đều tốt cho các ứng dụng có lưu lượng cao / hiệu suất cao. Điều quan trọng là làm thế nào bạn sử dụng nó. Dưới đây là so sánh nhanh về các khung phổ biến nhất và các tính năng mà chúng cung cấp về hiệu suất (chú thích: N = Không được hỗ trợ, P = Một phần, Y = có / được hỗ trợ):

                                | L2S | EF1 | EF4 | NH3 | ADO
                                +-----+-----+-----+-----+-----
Lazy Loading (entities)         |  N  |  N  |  N  |  Y  |  N
Lazy Loading (relationships)    |  Y  |  Y  |  Y  |  Y  |  N
Eager Loading (global)          |  N  |  N  |  N  |  Y  |  N
Eager Loading (per-session)     |  Y  |  N  |  N  |  Y  |  N
Eager Loading (per-query)       |  N  |  Y  |  Y  |  Y  |  Y
Level 1 (Identity) Cache        |  Y  |  Y  |  Y  |  Y  |  N
Level 2 (Query) Cache           |  N  |  N  |  P  |  Y  |  N
Compiled Queries                |  Y  |  P  |  Y  |  N  | N/A
Multi-Queries                   |  N  |  N  |  N  |  Y  |  Y
Multiple Result Sets            |  Y  |  N  |  P  |  Y  |  Y
Futures                         |  N  |  N  |  N  |  Y  |  N
Explicit Locking (per-table)    |  N  |  N  |  N  |  P  |  Y
Transaction Isolation Level     |  Y  |  Y  |  Y  |  Y  |  Y
Ad-Hoc Queries                  |  Y  |  P  |  Y  |  Y  |  Y
Stored Procedures               |  Y  |  P  |  Y  |  Y  |  Y
Unit of Work                    |  Y  |  Y  |  Y  |  Y  |  N

Như bạn có thể thấy, EF4 (phiên bản hiện tại) không quá tệ, nhưng có lẽ nó không phải là tốt nhất nếu hiệu suất là mối quan tâm chính của bạn. NHibernate đã trưởng thành hơn rất nhiều trong lĩnh vực này và thậm chí Linq to SQL cung cấp một số tính năng nâng cao hiệu suất mà EF vẫn không có. ADO.NET thô thường sẽ nhanh hơn cho các kịch bản truy cập dữ liệu rất cụ thể , nhưng, khi bạn đặt tất cả các phần lại với nhau, nó thực sự không mang lại nhiều lợi ích quan trọng mà bạn nhận được từ các khung khác nhau.

Và, chỉ để đảm bảo hoàn toàn chắc chắn rằng tôi nghe giống như một bản ghi bị hỏng, không có vấn đề nào trong một chút nếu bạn không thiết kế cơ sở dữ liệu, ứng dụng và chiến lược truy cập dữ liệu của mình đúng cách. Tất cả các mục trong biểu đồ trên là để cải thiện hiệu suất vượt quá mức cơ bản; hầu hết thời gian, đường cơ sở là những gì cần cải thiện nhất.


38
Thật là một câu trả lời tuyệt vời và toàn diện!

2
+1 (nhiều hơn nếu tôi có thể) - một trong những câu trả lời hay nhất tôi từng thấy ở đây và tôi đã học được một hoặc hai điều - cảm ơn vì đã chia sẻ điều này!
BrokenGlass

1
Đây là câu trả lời tuyệt vời ngay cả khi tôi không đồng ý với mọi thứ được đề cập. Bảng so sánh ORM không phải lúc nào cũng đúng. Thực thể lười tải là gì? Bạn có nghĩa là lười biếng tải cột? Điều đó được hỗ trợ trong L2S. Tại sao bạn nghĩ NH không hỗ trợ các truy vấn được biên dịch? Tôi nghĩ các truy vấn HQL có tên có thể được biên dịch trước. EF4 không hỗ trợ cho nhiều bộ kết quả.
Ladislav Mrnka

11
Tôi phải mạnh mẽ không đồng ý với không đủ tiêu chuẩn "EF là hoàn toàn tốt cho các ứng dụng có lưu lượng cao / hiệu năng cao" tuyên bố, chúng tôi đã nhìn thấy nhiều lần rằng đây không phải là trường hợp. Cấp, có thể chúng tôi không đồng ý với "hiệu suất cao" nghĩa là gì, nhưng ví dụ: tối ưu hóa các trang web xuống còn 500ms và có 400ms trong số đó không thể giải thích được trong khung (và chỉ 10ms thực sự nhấn SQL) trong một số trường hợp, nó hoàn toàn không thể chấp nhận được đối với nhóm nhà phát triển của chúng tôi.
Nick Craver

1
Lưu ý đơn giản về tương lai trong EF. Chúng không được cung cấp chính thức bởi nhóm MS EF nhưng có thể đạt được thông qua các dự án của bên thứ ba xác định các tiện ích mở rộng Tương lai <> cho IQueryable <>. Ví dụ: EntityFramework.Extends by LoreSoft, có sẵn trong NuGet. Các thử nghiệm cá nhân của tôi trong các ứng dụng sản xuất cho thấy hiệu suất tăng lên gấp 10 lần khi đóng gói hàng tá truy vấn không phụ thuộc (tất cả các truy vấn có thể được thực hiện song song, không ai yêu cầu kết quả của một truy vấn trước đó) trong một đợt sử dụng Tương lai. Ngoài ra AsNoTracking () cải thiện hiệu suất rất nhiều khi chỉ đọc nhiều bản ghi, không cập nhật sau này.
David Oliván Ubieto

38

Chỉnh sửa: Dựa trên câu trả lời tuyệt vời của @Aaronaught Tôi đang thêm vài điểm nhắm mục tiêu hiệu suất với EF. Những điểm mới được tiền tố bởi Chỉnh sửa.


Cải thiện lớn nhất về hiệu suất trong các trang web lưu lượng truy cập cao đạt được bằng cách lưu vào bộ đệm (= trước hết là tránh mọi xử lý máy chủ web hoặc truy vấn cơ sở dữ liệu), sau đó là xử lý không đồng bộ để tránh chặn luồng trong khi thực hiện truy vấn cơ sở dữ liệu.

Không có câu trả lời bằng chứng cho câu hỏi của bạn bởi vì nó luôn phụ thuộc vào yêu cầu cho ứng dụng và mức độ phức tạp của các truy vấn. Sự thật là năng suất của nhà phát triển với EF che giấu sự phức tạp đằng sau mà trong nhiều trường hợp dẫn đến việc sử dụng EF không chính xác và hiệu suất khủng khiếp. Ý tưởng rằng bạn có thể hiển thị giao diện trừu tượng hóa cấp cao để truy cập dữ liệu và nó sẽ hoạt động trơn tru trong mọi trường hợp không hoạt động. Ngay cả với ORM, bạn phải biết những gì đang xảy ra đằng sau sự trừu tượng và cách sử dụng nó một cách chính xác.

Nếu bạn không có kinh nghiệm trước đây với EF, bạn sẽ gặp rất nhiều thách thức khi xử lý hiệu suất. Bạn có thể mắc nhiều lỗi hơn khi làm việc với EF so với ADO.NET. Ngoài ra, có rất nhiều xử lý bổ sung được thực hiện trong EF, do đó, EF sẽ luôn chậm hơn đáng kể so với ADO.NET bản địa - đó là điều bạn có thể đo lường bằng cách chứng minh đơn giản về ứng dụng khái niệm.

Nếu bạn muốn có được hiệu suất tốt nhất từ ​​EF, rất có thể bạn sẽ phải:

  • Rất cẩn thận sửa đổi quyền truy cập dữ liệu của bạn với trình lược tả SQL và xem xét các truy vấn LINQ của bạn nếu họ sử dụng chính xác Linq-to-entity thay vì Linq-to-object
  • Rất cẩn thận sử dụng các tính năng tối ưu hóa tiên tiến của EF như MergeOption.NoTracking
  • Sử dụng ESQL trong một số trường hợp
  • Các truy vấn biên dịch trước được thực thi thường xuyên
  • Hãy suy nghĩ về việc tận dụng lợi thế của trình bao bọc bộ đệm EF để có được tính năng như "bộ đệm cấp hai" cho một số truy vấn
  • Sử dụng các khung nhìn SQL hoặc các truy vấn SQL được ánh xạ tùy chỉnh (yêu cầu duy trì thủ công tệp EDMX) trong một số trường hợp cho các phép chiếu hoặc tập hợp thường được sử dụng cần cải thiện hiệu suất
  • Sử dụng SQL gốc và các thủ tục được lưu trữ cho một số truy vấn không cung cấp đủ hiệu suất khi được xác định trong Linq hoặc ESQL
  • Chỉnh sửa: Sử dụng cẩn thận các truy vấn - mỗi truy vấn tạo vòng tròn riêng biệt cho cơ sở dữ liệu. EFv4 không có lô truy vấn vì nó không thể sử dụng nhiều tập kết quả cho mỗi lệnh cơ sở dữ liệu được thực thi. EFv4.5 sẽ hỗ trợ nhiều bộ kết quả cho các thủ tục lưu trữ được ánh xạ.
  • Chỉnh sửa: Làm việc cẩn thận với sửa đổi dữ liệu. Một lần nữa, EF hoàn toàn thiếu lệnh trộn . Vì vậy, trong ADO.NET, bạn có thể sử dụng một lần SqlCommandchứa nhiều lần chèn, cập nhật hoặc xóa nhưng với EF, mỗi lệnh như vậy sẽ được thực hiện trong roundtrip riêng cho cơ sở dữ liệu.
  • Chỉnh sửa: Làm việc cẩn thận với bản đồ nhận dạng / bộ đệm nhận dạng. EF có phương thức đặc biệt ( GetByKeytrong API ObjectContext hoặc FindAPI DbContext) để truy vấn bộ đệm trước. Nếu bạn sử dụng Linq-to-entity hoặc ESQL, nó sẽ tạo roundtrip cho cơ sở dữ liệu và sau đó nó sẽ trả về thể hiện hiện tại từ bộ đệm.
  • Chỉnh sửa: Cẩn thận sử dụng tải háo hức. Nó không phải luôn luôn là giải pháp win-win vì nó tạo ra một bộ dữ liệu khổng lồ . Như bạn có thể thấy nó là rất nhiều phức tạp bổ sung và đó là toàn bộ điểm. ORM làm cho việc lập bản đồ và vật chất hóa đơn giản hơn nhưng khi xử lý hiệu năng sẽ khiến nó phức tạp hơn nhiều và bạn sẽ phải đánh đổi.

Tôi không chắc nếu SO vẫn đang sử dụng L2S. Họ đã phát triển ORM mã nguồn mở mới có tên Dapper và tôi nghĩ rằng điểm chính đằng sau sự phát triển này là tăng hiệu suất.


Ladislav, đó là một câu trả lời thực sự hữu ích. Đây là lần đầu tiên tôi nghe về Dapper (và do đó đã phát hiện ra PetaPoco, Massive) - và nó có vẻ như là một ý tưởng thú vị.

1
SO dường như sử dụng kết hợp LINQ với SQL và Dapper ngay bây giờ: samsaffron.com/archive/2011/03/30/ Lời trích dẫn: "Chúng tôi đang sử dụng ORM [Dapper] mới của chúng tôi cho một vấn đề cụ thể: ánh xạ SQL được tham số hóa cho các đối tượng kinh doanh Chúng tôi không sử dụng nó như một ORM đầy đủ. Nó không tạo ra các mối quan hệ và tiếng chuông và tiếng huýt sáo khác. Điều này cho phép chúng tôi tiếp tục sử dụng LINQ-2-SQL khi hiệu suất không quan trọng và chuyển tất cả SQL nội tuyến của chúng tôi để sử dụng trình ánh xạ của chúng tôi, vì nó nhanh hơn và linh hoạt hơn. "

5
@Slauma tốt, đó là một tuyên bố từ nhiều tháng trước, nói chung tất cả các công việc mới về SO đều được thực hiện trong Dapper, ví dụ một bảng mới tôi đã thêm ngày hôm nay thậm chí không có trong tệp dbml.
Sam Saffron

1
@Sam: Có bài đăng blog mới về chiến lược truy cập dữ liệu hiện tại trên SO không? Sẽ rất thú vị! Dapper đã được gia hạn trong thời gian đó chưa? Tôi hiểu rằng Dapper không phải là một ORM hoàn chỉnh, không hỗ trợ các mối quan hệ - và những gì về cập nhật, chèn, xóa, giao dịch, theo dõi thay đổi, v.v.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.