Entity Framework เหมาะสมกับเว็บไซต์ที่มีการเข้าชมสูงหรือไม่


176

Entity Framework 4 เป็นทางออกที่ดีสำหรับเว็บไซต์สาธารณะที่มีความน่าจะเป็น 1,000 ครั้ง / วินาทีหรือไม่

ในความเข้าใจของฉัน EF เป็นโซลูชั่นที่ทำงานได้สำหรับเว็บไซต์ขนาดเล็กหรืออินทราเน็ต แต่จะไม่ขยายได้ง่ายสำหรับเว็บไซต์ชุมชนยอดนิยม (ฉันรู้ว่า SO ใช้ LINQ กับ SQL แต่ .. ฉันต้องการตัวอย่าง / หลักฐานเพิ่มเติม .. )

ตอนนี้ฉันกำลังยืนอยู่ที่สี่แยกของทั้งการเลือกวิธีการ ADO.NET ที่บริสุทธิ์หรือ EF4 คุณคิดว่าประสิทธิภาพของนักพัฒนาซอฟต์แวร์ที่พัฒนาขึ้นกับ EF นั้นคุ้มค่ากับประสิทธิภาพที่สูญเสียไปหรือการเข้าถึง ADO.NET แบบละเอียด (ด้วยขั้นตอนการจัดเก็บ) หรือไม่? มีปัญหาร้ายแรงใด ๆ ที่เว็บไซต์ที่มีปริมาณการใช้งานสูงอาจประสบกับปัญหานี้หรือไม่โดยใช้ EF?

ขอบคุณล่วงหน้า.


1
คุณไม่เข้าใจการปรับขนาด การปรับขนาดหมายถึงการเพิ่มปริมาณงาน 10 เท่าเมื่อคุณเพิ่มความจุ 10x เหตุใด EF จึงป้องกันสิ่งนี้ไม่ให้เกิดขึ้น มันเพิ่มปัจจัยค่าใช้จ่ายคงที่กับภาระงานฐานข้อมูลใด ๆ
usr

คำตอบ:


152

มันขึ้นอยู่กับบิตเกี่ยวกับวิธีการที่เป็นนามธรรมมากที่คุณต้องการ ทุกอย่างเป็นการประนีประนอม ตัวอย่างเช่น EF และ NHibernate แนะนำความยืดหยุ่นที่ดีสำหรับการแสดงข้อมูลในรูปแบบที่น่าสนใจและแปลกใหม่ - แต่เป็นผลพวกเขาจะเพิ่มค่าใช้จ่าย ค่าใช้จ่ายที่เห็นได้ชัดเจน

หากคุณไม่จำเป็นต้องสลับไปมาระหว่างผู้ให้บริการฐานข้อมูลและเค้าโครงตารางต่อไคลเอ็นต์ที่แตกต่างกันและหากข้อมูลของคุณอ่านเป็นหลักและหากคุณไม่จำเป็นต้องใช้โมเดลเดียวกันใน EF, SSRS , บริการข้อมูล ADO.NET ฯลฯ - แล้วถ้าคุณต้องการประสิทธิภาพการทำงานที่แน่นอนเป็นตัวชี้วัดที่สำคัญของคุณที่คุณสามารถทำไกลยิ่งกว่าดูกระฉับกระเฉง ในการทดสอบของเราจากทั้ง LINQ-to-SQL และ EF เราพบว่า EF ช้าลงอย่างมากในแง่ของประสิทธิภาพการอ่านแบบดิบซึ่งน่าจะเป็นเพราะเลเยอร์ที่เป็นนามธรรม

ที่ SO เรามีความสนใจในประสิทธิภาพดิบและเรามีความยินดีที่ได้รับการพัฒนาจากการสูญเสียสิ่งที่เป็นนามธรรมเพื่อให้ได้ความเร็ว เช่นเครื่องมือหลักของเราสำหรับการสอบถามฐานข้อมูลเป็นกระฉับกระเฉง สิ่งนี้ยังช่วยให้เราสามารถใช้โมเดล LINQ-to-SQL ที่มีอยู่แล้ว แต่เพียงอย่างเดียว: มันเร็วกว่ามาก ในการทดสอบประสิทธิภาพการทำงานนั้นเป็นสิ่งที่มีประสิทธิภาพเช่นเดียวกับการเขียนโค้ด ADO.NET ทั้งหมด (พารามิเตอร์, ตัวอ่านข้อมูลและอื่น ๆ ) ด้วยตนเอง แต่ไม่มีความเสี่ยงในการทำให้ชื่อคอลัมน์ผิด อย่างไรก็ตามเป็นไปตาม SQL (แม้ว่าจะมีความสุขที่จะใช้ SPROCs หากเป็นพิษที่คุณเลือก) ข้อดีของการนี้คือไม่มีการประมวลผลเพิ่มเติมที่เกี่ยวข้อง แต่เป็นระบบสำหรับผู้ที่ชอบ SQL ซึ่งฉันพิจารณา: ไม่ใช่สิ่งเลวร้าย!

ตัวอย่างเช่นแบบสอบถามทั่วไปอาจเป็น:

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

ซึ่งมีความสะดวกปลอดภัยในการฉีด ฯลฯ - แต่ไม่มีตัวอ่านข้อมูลจำนวนมาก โปรดทราบว่าในขณะที่สามารถจัดการทั้งพาร์ติชันแนวนอนและแนวตั้งเพื่อโหลดโครงสร้างที่ซับซ้อนได้ แต่จะไม่รองรับการโหลดแบบสันหลังยาว (แต่: เราเป็นแฟนตัวยงของการโหลดที่ชัดเจนมาก - ประหลาดใจน้อยลง)

หมายเหตุในคำตอบนี้ฉันไม่ได้บอกว่า EF ไม่เหมาะสำหรับงานที่มีปริมาณมาก ง่ายๆ: ฉันรู้ว่าช่างทำช่างมันช่างมัน


25
+1 สำหรับ dapper การใช้ ORM ที่ซับซ้อนสำหรับโมเดลการอ่านนั้นไม่จำเป็น วิธีที่เราใช้ในขณะนี้คือการใช้ ORM สำหรับโมเดลโดเมนของเรา (ซึ่งสิ่งที่ ORM แฟนซีนั้นมีประโยชน์จริง ๆ ) และ dapper สำหรับโมเดลการอ่านของเรา สิ่งนี้ทำสำหรับแอปพลิเคชันที่เร็วสุด ๆ

2
@ Marc ขอขอบคุณสำหรับคำตอบที่ดี - ในที่สุดฉันก็สามารถตัดสินใจได้อย่างมั่นใจ! จะตรวจสอบรายละเอียดในภายหลังอย่างแน่นอน จริงๆชอบวิธีการที่มันเป็นเพียงหนึ่งไฟล์ :)

3
ฉันเขียน ORM ของตัวเองขึ้นมา มันช้า ฉันดูคนทำแผนที่แล้วก็ชอบมัน ตอนนี้ฉันใช้ dapper สำหรับการอ่านทั้งหมดและ ORM ของฉันเองสำหรับการแทรก (ซึ่งสนับสนุน FK, การทำธุรกรรมและทุกสิ่งที่ดี) มันเป็นรหัสที่อ่านง่ายที่สุดที่ฉันเคยเขียน

2
@ acidzombie24 dapper รองรับการทำธุรกรรมและส่วน contrib ของ dapper (ไม่ใช่ส่วนหนึ่งของการปรับใช้ nuget) กำลังได้รับตัวเลือกการแทรก ฯลฯ เพียงแค่พูดถึงความสมบูรณ์ ฉันดีใจที่ช่างทำตัวมีประโยชน์
Marc Gravell

1
@ ชื่อฉันไม่เคยทำหลักสูตรวิดีโอในหัวข้อใด ๆ มีวิดีโอบางส่วนอยู่ที่นั่น แต่ไม่ใช่โดยฉัน ฉันมักจะเป็นคนเขียนคำ
Marc Gravell

217

คำถาม "ฉันควรใช้ ORM ใด" คือการกำหนดเป้าหมายปลายสุดของภูเขาน้ำแข็งขนาดใหญ่เมื่อพูดถึงกลยุทธ์การเข้าถึงข้อมูลโดยรวมและการเพิ่มประสิทธิภาพในแอปพลิเคชันขนาดใหญ่

สิ่งต่อไปนี้ทั้งหมด ( ตามลำดับความสำคัญอย่างคร่าวๆ ) จะส่งผลกระทบต่อปริมาณงานและสิ่งเหล่านั้นได้รับการจัดการ (บางครั้งในวิธีที่ต่างกัน) โดยกรอบ ORM หลักส่วนใหญ่ออกมาที่นั่น:

  1. การออกแบบและการบำรุงรักษาฐานข้อมูล

    นี่คือขอบเขตที่กว้างที่สุดปัจจัยที่สำคัญที่สุดเพียงอย่างเดียวของทรูพุตของแอพพลิเคชั่นหรือเว็บไซต์ที่ขับเคลื่อนด้วยข้อมูลและมักจะถูกละเลยโดยโปรแกรมเมอร์

    หากคุณไม่ได้ใช้เทคนิคการทำให้เป็นมาตรฐานที่เหมาะสมเว็บไซต์ของคุณจะถูกลงโทษ หากคุณไม่มีคีย์หลักเกือบทุกคำค้นหาจะดูช้า หากคุณใช้รูปแบบการต่อต้านที่รู้จักกันดีเช่นการใช้ตารางสำหรับคู่คีย์ - ค่า (AKA Entity-Attribute-Value) โดยไม่มีเหตุผลคุณจะระเบิดจำนวนการอ่านและเขียนทางกายภาพ

    หากคุณไม่ได้ใช้ประโยชน์จากคุณสมบัติที่ฐานข้อมูลมอบให้คุณเช่นการบีบอัดหน้าการFILESTREAMจัดเก็บ (สำหรับข้อมูลไบนารี) SPARSEคอลัมน์hierarchyidสำหรับลำดับชั้นและอื่น ๆ (ตัวอย่าง SQL Server ทั้งหมด) คุณจะไม่เห็นตำแหน่งใกล้เคียง การแสดงที่คุณจะได้เห็น

    คุณควรเริ่มกังวลเกี่ยวกับกลยุทธ์การเข้าถึงข้อมูลของคุณหลังจากที่คุณออกแบบฐานข้อมูลและเชื่อมั่นในตัวเองว่าเป็นสิ่งที่ดีอย่างที่ควรจะเป็นในช่วงเวลานั้น

  2. กระตือรือร้นกับการโหลดขี้เกียจ

    ORM ส่วนใหญ่ใช้เทคนิคที่เรียกว่าlazy loadingสำหรับความสัมพันธ์ซึ่งหมายความว่าโดยค่าเริ่มต้นมันจะโหลดเอนทิตีหนึ่ง (แถวของตาราง) ในแต่ละครั้งและทำการเดินทางไปยังฐานข้อมูลทุกครั้งที่ต้องการโหลดหนึ่งหรือหลายอัน คีย์) แถว

    นี่ไม่ใช่สิ่งที่ดีหรือไม่ดีมันค่อนข้างขึ้นอยู่กับสิ่งที่จะทำจริงกับข้อมูลและเท่าไหร่ที่คุณรู้ล่วงหน้า บางครั้งการโหลดสันหลังยาวเป็นสิ่งที่ถูกต้อง ตัวอย่างเช่น NHibernate อาจตัดสินใจที่จะไม่ค้นหาสิ่งใดเลยและเพียงสร้างพร็อกซีสำหรับรหัสเฉพาะ หากทั้งหมดที่คุณต้องการคือรหัสตัวเองเหตุใดจึงควรถามเพิ่มเติม ในทางตรงกันข้ามถ้าคุณกำลังพยายามที่จะพิมพ์ต้นไม้ขององค์ประกอบเดียวทุกคนในลำดับชั้นที่ 3 ระดับขี้เกียจโหลดกลายเป็น O (N²) การดำเนินงานซึ่งเป็นอย่างมากที่ไม่ดีสำหรับการทำงาน

    ประโยชน์อย่างหนึ่งที่น่าสนใจในการใช้ "pure SQL" (เช่น raw แบบสอบถาม ADO.NET / กระบวนงานที่เก็บไว้) คือโดยพื้นฐานแล้วมันบังคับให้คุณคิดอย่างแม่นยำว่าข้อมูลใดที่จำเป็นต้องแสดงหน้าจอหรือหน้าใด ๆ คุณลักษณะ ORM และการโหลดแบบสันหลังยาวไม่ได้ป้องกันคุณจากการทำเช่นนี้ แต่จะทำให้คุณมีโอกาสที่จะ ... ดีขี้เกียจและไม่ได้ตั้งใจกระจายจำนวนข้อความค้นหาที่คุณเรียกใช้โดยไม่ตั้งใจ ดังนั้นคุณต้องเข้าใจถึงคุณสมบัติการโหลด ORM ของคุณและต้องระมัดระวังเกี่ยวกับจำนวนข้อความค้นหาที่คุณส่งไปยังเซิร์ฟเวอร์สำหรับคำขอหน้าใด ๆ

  3. เก็บเอาไว้

    ORM หลัก ๆ ทั้งหมดรักษาแคชระดับแรก AKA "รหัสประจำตัวแคช" ซึ่งหมายความว่าหากคุณร้องขอเอนทิตีเดียวกันสองครั้งด้วย ID ของมันก็ไม่จำเป็นต้องมีรอบที่สองและ (ถ้าคุณออกแบบฐานข้อมูลของคุณอย่างถูกต้อง ) ช่วยให้คุณสามารถใช้การทำงานพร้อมกันในแง่ดี

    แคช L1 นั้นค่อนข้างทึบใน L2S และ EF คุณต้องเชื่อว่ามันใช้งานได้ NHibernate มีความชัดเจนมากขึ้นเกี่ยวกับเรื่องนี้ ( Get/ Loadvs. Query/ QueryOver) อย่างไรก็ตามตราบใดที่คุณพยายามสืบค้นด้วย ID ให้มากที่สุดคุณควรจะทำได้ที่นี่ ผู้คนจำนวนมากลืมเกี่ยวกับแคช L1 และค้นหาเอนทิตีเดียวกันซ้ำ ๆ ซ้ำ ๆ โดยสิ่งอื่นที่ไม่ใช่ ID (เช่นฟิลด์ค้นหา) หากคุณต้องการทำสิ่งนี้คุณควรบันทึก ID หรือแม้แต่เอนทิตีทั้งหมดสำหรับการค้นหาในอนาคต

    นอกจากนี้ยังมีแคชระดับ 2 ("แคชแบบสอบถาม") NHibernate มีสิ่งนี้ในตัว Linq ไปยัง SQL และ Entity Framework มีการรวบรวมแบบสอบถามซึ่งสามารถช่วยลดแอปเซิร์ฟเวอร์โหลดได้ค่อนข้างน้อยด้วยการรวบรวมการแสดงออกของแบบสอบถามเอง แต่ไม่ได้แคชข้อมูล Microsoft ดูเหมือนจะพิจารณาเรื่องนี้เกี่ยวกับแอปพลิเคชั่นมากกว่าความกังวลเกี่ยวกับการเข้าถึงข้อมูลและนี่เป็นจุดอ่อนที่สำคัญของทั้ง L2S และ EF ไม่จำเป็นต้องบอกว่าเป็นจุดอ่อนของ SQL "raw" เพื่อให้ได้ประสิทธิภาพที่ดีจริง ๆ กับ ORM อื่น ๆ ที่ไม่ใช่ NHibernate คุณต้องติดตั้งหน้าแคชของคุณเอง

    นอกจากนี้ยังมี "ส่วนขยาย" แคช L2 สำหรับ EF4 ซึ่งก็โอเคแต่ไม่สามารถทดแทนแคชในระดับแอปพลิเคชันได้อย่างแท้จริง

  4. จำนวนคำค้นหา

    ฐานข้อมูลเชิงสัมพันธ์ขึ้นอยู่กับชุดของข้อมูล พวกเขากำลังดีจริงๆที่ผลิตขนาดใหญ่ปริมาณของข้อมูลในระยะเวลาสั้น ๆ แต่พวกเขาไม่ได้อยู่ใกล้เป็นดีในแง่ของการค้นหาความล่าช้าเพราะมีจำนวนหนึ่งของค่าใช้จ่ายที่เกี่ยวข้องในทุกคำสั่ง แอปที่ออกแบบมาอย่างดีควรเล่นกับจุดแข็งของ DBMS นี้และพยายามลดจำนวนการค้นหาและลดจำนวนข้อมูลในแต่ละรายการให้ได้มากที่สุด

    ตอนนี้ฉันไม่ได้กำลังสอบถามฐานข้อมูลทั้งหมดเมื่อคุณต้องการเพียงหนึ่งแถว สิ่งที่ฉันพูดคือถ้าคุณต้องการCustomer, Address, Phone, CreditCardและOrderแถวทั้งหมดในเวลาเดียวกันเพื่อให้บริการหน้าเดียวแล้วคุณควรจะถามสำหรับพวกเขาทั้งหมดในเวลาเดียวกันไม่ได้ดำเนินการค้นหาแต่ละครั้งแยกต่างหาก บางครั้งมันแย่กว่านั้นคุณจะเห็นรหัสที่ถามCustomerเร็กคอร์ดเดียวกัน5 ครั้งติดต่อกันก่อนที่จะได้รับIdแล้วNameจากEmailAddressนั้นก็จากนั้น ... มันไม่มีประสิทธิภาพอย่างน่าขัน

    แม้ว่าคุณจะต้องเรียกใช้คิวรีหลายชุดที่ทำงานทั้งหมดในชุดข้อมูลที่แตกต่างกันอย่างสมบูรณ์ แต่ก็มักจะมีประสิทธิภาพมากกว่าในการส่งทั้งหมดไปยังฐานข้อมูลในรูปแบบ "สคริปต์" เดียวและส่งคืนชุดผลลัพธ์หลายชุด เป็นค่าใช้จ่ายที่คุณกังวลไม่ใช่ปริมาณข้อมูลทั้งหมด

    สิ่งนี้อาจฟังดูเป็นเรื่องสามัญ แต่บ่อยครั้งที่ง่ายต่อการติดตามการสืบค้นทั้งหมดที่ถูกดำเนินการในส่วนต่าง ๆ ของแอปพลิเคชัน ผู้ให้บริการสมาชิกของคุณทำการสอบถามผู้ใช้ / ตารางบทบาท, การดำเนินการส่วนหัวของคุณจะสอบถามตะกร้าสินค้า, การกระทำเมนูของคุณจะค้นหาตารางแผนที่เว็บไซต์, การกระทำแถบด้านข้างของคุณสอบถามรายการผลิตภัณฑ์ที่โดดเด่นแล้วอาจแบ่งหน้าของคุณออกเป็น สอบถามประวัติการสั่งซื้อ, ดูล่าสุด, หมวดหมู่และตารางสินค้าคงคลังแยกจากกันและก่อนที่คุณจะรู้คุณกำลังดำเนินการค้นหา 20 ครั้งก่อนที่คุณจะสามารถเริ่มแสดงหน้าได้ มันทำลายประสิทธิภาพอย่างเต็มที่

    กรอบบางอย่าง - และฉันคิดว่าส่วนใหญ่ของ NHibernate ที่นี่ - มีความฉลาดอย่างเหลือเชื่อเกี่ยวกับเรื่องนี้และให้คุณใช้สิ่งที่เรียกว่าฟิวเจอร์สที่รวบรวมคำถามทั้งหมดและพยายามที่จะดำเนินการทั้งหมดในครั้งเดียวในนาทีสุดท้าย AFAIK คุณเป็นเจ้าของด้วยตัวคุณเองถ้าคุณต้องการทำสิ่งนี้ด้วยเทคโนโลยีใด ๆ ของ Microsoft คุณต้องสร้างมันเป็นตรรกะแอปพลิเคชันของคุณ

  5. การทำดัชนีเพรดิเคตและเส้นโครง

    อย่างน้อย 50% ของ devs ที่ฉันพูดถึงและแม้แต่ DBA บางคนก็ดูเหมือนจะมีปัญหากับแนวคิดของการครอบคลุมดัชนี พวกเขาคิดว่า " Customer.Nameเอาล่ะคอลัมน์ถูกจัดทำดัชนีดังนั้นการค้นหาทุกครั้งที่ฉันทำกับชื่อควรเร็ว" ยกเว้นว่าจะไม่ทำงานเช่นนั้นเว้นแต่Nameดัชนีจะครอบคลุมคอลัมน์เฉพาะที่คุณกำลังค้นหา ใน SQL Server เสร็จแล้วINCLUDEในCREATE INDEXคำสั่ง

    หากคุณใช้SELECT *ทุกที่ - และนั่นคือสิ่งที่ ORM ทุกอย่างจะทำมากไปกว่านั้นเว้นแต่คุณจะระบุเป็นอย่างอื่นอย่างชัดเจนโดยใช้การฉายภาพแล้ว DBMS อาจเลือกที่จะละเว้นดัชนีของคุณอย่างสมบูรณ์เพราะมีคอลัมน์ที่ไม่ครอบคลุม การฉายภาพหมายถึงยกตัวอย่างเช่นแทนที่จะทำสิ่งนี้:

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

    คุณทำสิ่งนี้แทน:

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

    และจะนี้สำหรับ ORMs ทันสมัยที่สุดสั่งให้ไปเท่านั้นและสอบถามIdและNameคอลัมน์ที่ได้รับความคุ้มครองโดยสันนิษฐานดัชนี ( แต่ไม่Email, LastActivityDateหรืออะไรก็ตามคอลัมน์อื่น ๆ ที่คุณเกิดขึ้นที่จะติดอยู่ในนั้น)

    นอกจากนี้ยังง่ายมากที่จะกระจายผลประโยชน์จากการจัดทำดัชนีโดยใช้ภาคแสดงที่ไม่เหมาะสม ตัวอย่างเช่น:

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

    ... มีลักษณะเกือบจะเหมือนกับคำก่อนหน้าของเรา LIKE '%Doe%'แต่ในความเป็นจริงจะมีผลในตารางหรือดัชนีสแกนเพราะมันแปลว่า ในทำนองเดียวกันแบบสอบถามอื่นที่ดูเรียบง่ายอย่างน่าสงสัยคือ:

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

    สมมติว่าคุณมีดัชนีในภาคBirthDateแสดงนี้มีโอกาสที่ดีที่จะทำให้มันไร้ประโยชน์อย่างสมบูรณ์ โปรแกรมเมอร์สมมุติฐานของเราที่นี่พยายามสร้างแบบสอบถามแบบไดนามิก ("กรองวันเกิดเฉพาะในกรณีที่ระบุพารามิเตอร์") แต่นี่ไม่ใช่วิธีที่ถูกต้องที่จะทำ เขียนแบบนี้แทน:

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

    ... ตอนนี้โปรแกรม DB รู้วิธีการทำให้เป็นพารามิเตอร์นี้และทำดัชนีการค้นหา การเปลี่ยนแปลงเล็กน้อยในนิพจน์เคียวรีเล็กน้อยหนึ่งรายการอาจส่งผลกระทบต่อประสิทธิภาพการทำงานอย่างมาก

    น่าเสียดายที่ LINQ โดยทั่วไปทำให้ทุกอย่างง่ายเกินไปในการเขียนข้อความค้นหาที่ไม่ดีเช่นนี้เพราะบางครั้งผู้ให้บริการจะสามารถเดาได้ว่าคุณพยายามทำอะไรและเพิ่มประสิทธิภาพของแบบสอบถามและบางครั้งพวกเขาก็ไม่ได้ ดังนั้นคุณจะได้ผลลัพธ์ที่ไม่สอดคล้องอย่างน่าผิดหวังซึ่งจะทำให้เห็นได้อย่างชัดเจน (สำหรับ DBA ที่มีประสบการณ์แล้ว) คุณเพิ่งเขียน SQL แบบเก่าธรรมดา

    โดยพื้นฐานแล้วทั้งหมดนั้นเกิดขึ้นจากข้อเท็จจริงที่ว่าคุณต้องจับตาดูทั้ง SQL ที่สร้างขึ้นและแผนการดำเนินการที่พวกเขานำไปสู่และหากคุณไม่ได้รับผลลัพธ์ตามที่คาดหวังอย่ากลัวที่จะหลีกเลี่ยง ORM เลเยอร์นาน ๆ และโค้ด SQL สิ่งนี้จะเกิดขึ้นกับORM ใด ๆไม่ใช่แค่ EF

  6. การทำธุรกรรมและการล็อค

    คุณต้องการแสดงข้อมูลที่เป็นปัจจุบันถึงมิลลิวินาทีหรือไม่ อาจจะ - มันขึ้นอยู่กับ - แต่อาจจะไม่ น่าเศร้าEntity Framework ไม่ได้ให้คุณnolockคุณสามารถใช้ได้เฉพาะREAD UNCOMMITTEDในระดับธุรกรรม (ไม่ใช่ระดับตาราง) ในความเป็นจริงไม่มี ORM ใดที่เชื่อถือได้เป็นพิเศษเกี่ยวกับเรื่องนี้ หากคุณต้องการอ่านสกปรกคุณต้องเลื่อนลงไปที่ระดับ SQL และเขียนแบบสอบถามเฉพาะกิจหรือขั้นตอนการจัดเก็บ ดังนั้นสิ่งที่เดือดร้อนลงไปอีกคือความง่ายสำหรับคุณที่จะทำสิ่งนั้นภายในกรอบ

    Entity Framework มาไกลในเรื่องนี้ - รุ่นที่ 1 ของ EF (ใน. NET 3.5) แย่มากทำให้ยากที่จะฝ่าฝืน "เอนทิตี้" สิ่งที่เป็นนามธรรม แต่ตอนนี้คุณมีExecuteStoreQueryและTranslateแล้วจริงๆ ก็ไม่เลวนะ. ทำความรู้จักกับคนเหล่านี้เพราะคุณจะใช้พวกเขาอย่างมาก

    นอกจากนี้ยังมีปัญหาเรื่องการล็อกการเขียนและการหยุดชะงักและวิธีปฏิบัติทั่วไปของการล็อกโฮลดิ้งในฐานข้อมูลสำหรับเวลาน้อยที่สุดเท่าที่จะทำได้ ในเรื่องนี้ ORMs (รวมถึง Entity Framework) จริงมีแนวโน้มที่จะดีขึ้นกว่า SQL ดิบเพราะพวกเขาแค็ปซูหน่วยงานรูปแบบซึ่งใน EF เป็นSaveChanges กล่าวอีกนัยหนึ่งคุณสามารถ "แทรก" หรือ "อัปเดต" หรือ "ลบ" หน่วยงานลงในเนื้อหาของหัวใจของคุณได้ทุกเมื่อที่คุณต้องการความปลอดภัยในความรู้ที่ไม่มีการเปลี่ยนแปลงใด ๆ จะถูกผลักไปยังฐานข้อมูล

    โปรดทราบว่า UOW ไม่เหมือนกับธุรกรรมที่ใช้เวลานาน UOW ยังคงใช้คุณสมบัติการทำงานพร้อมกันในแง่ของการออมและติดตามการเปลี่ยนแปลงทั้งหมดในหน่วยความจำ ไม่ใช่คำสั่ง DML เดียวที่ถูกปล่อยออกมาจนกว่าการกระทำสุดท้าย สิ่งนี้จะช่วยให้การทำธุรกรรมครั้งที่ต่ำที่สุด หากคุณสร้างแอปพลิเคชันของคุณโดยใช้ SQL ดิบมันค่อนข้างยากที่จะบรรลุพฤติกรรมที่เลื่อนออกไปนี้

    สิ่งนี้หมายความว่าอย่างไรสำหรับ EF โดยเฉพาะ: ทำให้หน่วยงานของคุณหยาบที่สุดเท่าที่จะเป็นไปได้และอย่าผูกมัดจนกว่าคุณจะต้องการจริงๆ ทำสิ่งนี้และคุณจะจบลงด้วยการช่วงชิงล็อกที่ต่ำกว่าที่คุณจะใช้คำสั่ง ADO.NET ทีละคำสั่งแบบสุ่ม

สรุปแล้ว:

EF นั้นสมบูรณ์แบบสำหรับแอพพลิเคชั่นที่มีปริมาณการใช้งานสูงและมีประสิทธิภาพเช่นเดียวกับกรอบอื่น ๆ ที่ใช้ได้กับแอพพลิเคชั่นที่มีปริมาณการใช้งานสูงและมีประสิทธิภาพสูง สิ่งที่สำคัญคือคุณใช้มันอย่างไร นี่เป็นการเปรียบเทียบอย่างรวดเร็วของเฟรมเวิร์กที่ได้รับความนิยมมากที่สุดและฟีเจอร์ที่เสนอในแง่ของประสิทธิภาพ (คำอธิบาย: N = ไม่รองรับ, P = บางส่วน, Y = ใช่ / รองรับ):

                                | 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

อย่างที่คุณเห็น EF4 (เวอร์ชั่นปัจจุบัน) ไม่ได้เลวร้ายนัก แต่มันอาจจะไม่ดีที่สุดถ้าประสิทธิภาพเป็นประเด็นหลักของคุณ NHibernate นั้นมีความเป็นผู้ใหญ่มากกว่าในพื้นที่นี้และแม้แต่ Linq to SQL ยังมีฟีเจอร์ที่ช่วยเพิ่มประสิทธิภาพที่ EF ยังไม่ได้ทำ Raw ADO.NET มักจะเร็วขึ้นสำหรับสถานการณ์การเข้าถึงข้อมูลที่เฉพาะเจาะจงมากแต่เมื่อคุณรวมทุกส่วนเข้าด้วยกันจริงๆแล้วมันไม่ได้ให้ประโยชน์ที่สำคัญมากมายที่คุณได้รับจากกรอบงานที่หลากหลาย

และเพื่อให้แน่ใจอย่างสมบูรณ์ว่าฉันดูเหมือนว่าจะไม่มีการบันทึกใด ๆ เลยสิ่งนี้จะน้อยมากหากคุณไม่ได้ออกแบบฐานข้อมูลแอปพลิเคชันและกลยุทธ์การเข้าถึงข้อมูลอย่างถูกต้อง รายการทั้งหมดในแผนภูมิด้านบนมีไว้เพื่อปรับปรุงประสิทธิภาพที่เหนือกว่าพื้นฐาน เวลาส่วนใหญ่ตัวพื้นฐานเป็นสิ่งที่ต้องการการปรับปรุงมากที่สุด


38
ช่างเป็นคำตอบที่ยอดเยี่ยมและครอบคลุม!

2
+1 (ยิ่งถ้าทำได้) - หนึ่งในคำตอบที่ดีที่สุดที่ฉันเคยเห็นในขณะนี้และฉันเรียนรู้สิ่งหนึ่งหรือสองอย่าง - ขอบคุณที่แบ่งปันสิ่งนี้!
BrokenGlass

1
นี่คือคำตอบที่ดีแม้ว่าฉันจะไม่เห็นด้วยกับทุกสิ่งที่กล่าวถึง ตารางการเปรียบเทียบ ORM ไม่ถูกต้องเสมอไป เอนทิตีโหลดขี้เกียจคืออะไร? คุณหมายถึงคอลัมน์ที่ขี้เกียจหรือเปล่า ที่รองรับใน L2S ทำไมคุณคิดว่า NH ไม่สนับสนุนการสืบค้นที่คอมไพล์? ฉันคิดว่าชื่อแบบสอบถาม HQL สามารถรวบรวมไว้ล่วงหน้า EF4 ไม่รองรับชุดผลลัพธ์หลายชุด
Ladislav Mrnka

11
ฉันต้องไม่เห็นด้วยอย่างยิ่งกับคำสั่ง "EF นั้นใช้ได้ดีสำหรับแอปพลิเคชันที่มีการรับส่งข้อมูลสูง / ประสิทธิภาพสูง" เราได้เห็นซ้ำแล้วซ้ำอีกว่านี่ไม่ใช่กรณี จริงอยู่ที่เราอาจจะไม่เห็นด้วยกับสิ่งที่ "ประสิทธิภาพสูง" หมายถึง แต่สำหรับตัวอย่างเช่นการเพิ่มประสิทธิภาพหน้าเว็บลงไป 500ms และมี 400 มิลลิวินาที + ที่ใช้เวลาอย่างลึกลับภายในกรอบ (และ 10ms จริงตี SQL) ไม่ได้ "ดี" สำหรับบางสถานการณ์ มันเป็นเรื่องที่ยอมรับไม่ได้สำหรับทีมนักพัฒนาของเรา
Nick Craver

1
เรื่องย่อเกี่ยวกับอนาคตของ EF พวกเขาไม่ได้ให้บริการอย่างเป็นทางการโดยทีม MS EF แต่สามารถทำได้ผ่านโครงการของบุคคลที่สามซึ่งกำหนดส่วนขยายในอนาคต <> เป็น IQueryable <> ตัวอย่างเช่น EntityFramework.Extended โดย LoreSoft มีอยู่ใน NuGet การทดสอบส่วนตัวของฉันในแอปพลิเคชั่นการผลิตแสดงให้ได้ประสิทธิภาพมากขึ้นถึง 10 เท่าเมื่อบรรจุคำค้นหาที่ไม่ขึ้นอยู่กับหลายสิบชุด (แบบสอบถามทั้งหมดสามารถดำเนินการแบบขนานได้โดยไม่ต้องใช้ผลลัพธ์จากรุ่นก่อนหน้า) ในชุดเดียว AsNoTracking () ยังช่วยปรับปรุงประสิทธิภาพการทำงานเป็นอย่างมากเมื่อเพียงแค่อ่านบันทึกจำนวนมากไม่ได้อัพเดตในภายหลัง
David Oliván Ubieto

38

แก้ไข:อิงจาก @Aaronaught คำตอบที่ดีฉันกำลังเพิ่มคะแนนการกำหนดเป้าหมายไปที่ EF ไม่กี่คะแนน คะแนนใหม่เหล่านั้นนำหน้าด้วยการแก้ไข


การปรับปรุงประสิทธิภาพที่ใหญ่ที่สุดในเว็บไซต์ที่มีการรับส่งข้อมูลสูงทำได้โดยการแคช (= ก่อนอื่นหลีกเลี่ยงการประมวลผลเว็บเซิร์ฟเวอร์หรือการสืบค้นฐานข้อมูล) ตามด้วยการประมวลผลแบบอะซิงโครนัสเพื่อหลีกเลี่ยงการบล็อกเธรด

ไม่มีคำตอบที่ถูกต้องสำหรับคำถามของคุณเพราะมันขึ้นอยู่กับข้อกำหนดสำหรับการสมัครและความซับซ้อนของการค้นหา ความจริงก็คือประสิทธิภาพการทำงานของนักพัฒนาซอฟต์แวร์กับ EF นั้นซ่อนความซับซ้อนไว้ซึ่งในหลายกรณีทำให้การใช้งาน EF ไม่ถูกต้องและประสิทธิภาพแย่มาก แนวคิดที่ว่าคุณสามารถเปิดเผยส่วนต่อประสานที่เป็นนามธรรมระดับสูงสำหรับการเข้าถึงข้อมูลและมันจะทำงานได้อย่างราบรื่นในทุกกรณีไม่ทำงาน แม้แต่กับ ORM คุณต้องรู้ว่าเกิดอะไรขึ้นหลังสิ่งที่เป็นนามธรรมและวิธีการใช้อย่างถูกต้อง

หากคุณไม่เคยมีประสบการณ์กับ EF มาก่อนคุณจะพบกับความท้าทายมากมายเมื่อจัดการกับประสิทธิภาพ คุณสามารถทำผิดพลาดได้มากขึ้นเมื่อทำงานกับ EF เปรียบเทียบกับ ADO.NET นอกจากนี้ยังมีการประมวลผลเพิ่มเติมจำนวนมากที่ทำใน EF ดังนั้น EF จะช้ากว่า ADO.NET ดั้งเดิมอย่างมาก - นั่นคือสิ่งที่คุณสามารถวัดได้โดยพิสูจน์การประยุกต์แนวคิดอย่างง่าย

หากคุณต้องการประสิทธิภาพที่ดีที่สุดจาก EF คุณอาจต้อง:

  • แก้ไขการเข้าถึงข้อมูลด้วย SQL profiler อย่างละเอียดและตรวจสอบคำสั่ง LINQ ของคุณหากใช้ Linq-to-entity แทน Linq-to-objects อย่างถูกต้อง
  • ใช้คุณสมบัติการเพิ่มประสิทธิภาพขั้นสูงของ EF อย่างระมัดระวังมาก MergeOption.NoTracking
  • ใช้ ESQL ในบางกรณี
  • รวบรวมคำสั่งล่วงหน้าซึ่งดำเนินการบ่อยครั้ง
  • คิดเกี่ยวกับการใช้ประโยชน์จากกระดาษห่อของ EF Caching เพื่อรับ "แคชระดับสอง" เช่นฟีเจอร์สำหรับการค้นหา
  • ใช้มุมมอง SQL หรือแบบสอบถาม SQL ที่แมปแบบกำหนดเอง (ต้องมีการบำรุงรักษาด้วยตนเองของไฟล์ EDMX) ในบางสถานการณ์สำหรับการคาดการณ์หรือการรวมที่ใช้บ่อยซึ่งต้องการการปรับปรุงประสิทธิภาพ
  • ใช้ Native SQL และกระบวนงานที่เก็บไว้สำหรับแบบสอบถามบางอย่างที่ไม่มีประสิทธิภาพเพียงพอเมื่อกำหนดไว้ใน Linq หรือ ESQL
  • แก้ไข:ใช้แบบสอบถามอย่างระมัดระวัง - ทุกแบบสอบถามทำให้ไปกลับแยกต่างหากไปยังฐานข้อมูล EFv4 ไม่มีการแบตช์แบบสอบถามเนื่องจากไม่สามารถใช้ชุดผลลัพธ์หลายชุดต่อคำสั่งฐานข้อมูลที่ดำเนินการ EFv4.5 จะสนับสนุนชุดผลลัพธ์หลายชุดสำหรับกระบวนงานที่เก็บแบบแมป
  • แก้ไข:ทำงานอย่างระมัดระวังด้วยการแก้ไขข้อมูล อีกครั้งEF สมบูรณ์ขาด batching ดังนั้นใน ADO.NET คุณสามารถใช้ซิงเกิลSqlCommandที่มีการแทรกหลายครั้งการอัพเดตหรือการลบ แต่ด้วย EF ทุกคำสั่งดังกล่าวจะถูกดำเนินการแยกไปยังฐานข้อมูล
  • แก้ไข:ทำงานอย่างระมัดระวังกับ identity map / identity cache EF มีวิธีพิเศษ ( GetByKeyใน ObjectContext API หรือFindใน DbContext API) เพื่อสอบถามแคชก่อน ถ้าคุณใช้ Linq-to-entity หรือ ESQL มันจะสร้างไปที่ฐานข้อมูลและหลังจากนั้นมันจะส่งคืนอินสแตนซ์ที่มีอยู่จากแคช
  • แก้ไข:ใช้ความกระตือรือร้นในการโหลดอย่างระมัดระวัง มันเป็นเรื่องที่ไม่เคยชนะการแก้ปัญหาเพราะมันก่อให้หนึ่งชุดข้อมูลขนาดใหญ่ อย่างที่คุณเห็นมันมีความซับซ้อนมากขึ้นและนั่นคือประเด็นทั้งหมด ORM ทำให้การทำแผนที่และวัตถุเป็นเรื่องง่ายขึ้น แต่เมื่อจัดการกับประสิทธิภาพมันจะทำให้มันซับซ้อนมากขึ้นและคุณจะต้องทำการแลกเปลี่ยน

ฉันไม่แน่ใจว่าดังนั้นยังใช้ L2S พวกเขาพัฒนา ORM โอเพนซอร์สใหม่เรียกว่าDapperและฉันคิดว่าประเด็นหลักที่อยู่เบื้องหลังการพัฒนานี้คือการเพิ่มประสิทธิภาพ


Ladislav นั่นเป็นคำตอบที่มีประโยชน์จริงๆ นี่เป็นครั้งแรกที่ฉันได้ยินเกี่ยวกับ Dapper (และต่อมาค้นพบ PetaPoco, Massive) - และดูเหมือนว่าความคิดที่น่าสนใจ

1
ดังนั้นดูเหมือนว่าจะใช้การผสมผสานของ LINQ กับ SQL และ Dapper ในขณะนี้: samsaffron.com/archive/2011/03/30/ ......ข้อความอ้างอิง: "เรากำลังใช้ ORM [Dapper] ใหม่ของเราสำหรับปัญหาเฉพาะ: การแมปพารามิเตอร์ SQL กับวัตถุทางธุรกิจ เราไม่ได้ใช้มันเป็น ORM แบบเต็มมันไม่ได้ทำความสัมพันธ์และ bells และ whistles อื่น ๆ นี้ช่วยให้เราสามารถใช้ LINQ-2-SQL ต่อไปโดยที่ประสิทธิภาพไม่สำคัญและพอร์ตอินไลน์ SQL ทั้งหมดของเราเพื่อใช้ mapper ของเรา เพราะมันเร็วกว่าและยืดหยุ่นกว่า "

5
@Slauma ดีนั่นคือคำสั่งจากเดือนที่ผ่านมาโดยทั่วไปงานใหม่ทั้งหมดใน SO จะทำใน Dapper ตัวอย่างเช่นตารางใหม่ที่ฉันเพิ่มในวันนี้ไม่ได้อยู่ในไฟล์ dbml
Sam Saffron

1
@ แซม: มีบล็อกโพสต์ใหม่เกี่ยวกับกลยุทธ์การเข้าถึงข้อมูลปัจจุบันบน SO หรือไม่? จะน่าสนใจมาก ! Dapper ได้รับการขยายออกไปในระหว่างนี้หรือไม่? ความเข้าใจของฉันคือ Dapper ไม่ใช่ ORM ที่สมบูรณ์ไม่มีการสนับสนุนความสัมพันธ์ - และสิ่งที่เกี่ยวกับการปรับปรุงแทรกการลบธุรกรรมการติดตามการเปลี่ยนแปลง ฯลฯ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.