18

मैं इकाई फ्रेमवर्क के साथ एक दिलचस्प प्रदर्शन समस्या में भाग रहा हूं। मैं पहले कोड का उपयोग कर रहा हूँ।इकाई फ्रेमवर्क प्रदर्शन समस्या

एक पुस्तक में कई समीक्षा हो सकती है:

यहाँ मेरी संस्थाओं की संरचना है। एक समीक्षा एक पुस्तक से जुड़ी है। एक समीक्षा में एक या कई टिप्पणियां हो सकती हैं। एक टिप्पणी एक समीक्षा के साथ जुड़ा हुआ है।

public class Book 
{ 
    public int BookId { get; set; } 
    // ... 
    public ICollection<Review> Reviews { get; set; } 
} 

public class Review 
{ 
    public int ReviewId { get; set; } 
    public int BookId { get; set; } 
    public Book Book { get; set; } 
    public ICollection<Comment> Comments { get; set; } 
} 

public class Comment 
{ 
    public int CommentId { get; set; } 
    public int ReviewId { get; set; } 
    public Review Review { get; set; } 
} 

मैंने अपने डेटाबेस को बहुत सारे डेटा के साथ पॉप्युलेट किया और उचित इंडेक्स जोड़े।

var bookAndReviews = db.Books.Where(b => b.BookId == id) 
         .Include(b => b.Reviews) 
         .FirstOrDefault(); 

यह विशेष रूप से पुस्तक 10,000 समीक्षा है: मैं केवल एक पुस्तक है कि उस पर 10,000 समीक्षा इस क्वेरी का उपयोग किया गया है प्राप्त करने का प्रयास कर रहा हूँ। इस क्वेरी का प्रदर्शन लगभग 4 सेकंड है। सटीक एक ही क्वेरी (एसक्यूएल प्रोफाइलर के माध्यम से) चलाना वास्तव में किसी भी समय वास्तव में वापस नहीं आता है। मैंने डेटा को पुनर्प्राप्त करने के लिए एक ही क्वेरी और एक SQL डेटा एडाप्टर और कस्टम ऑब्जेक्ट्स का उपयोग किया और यह 500 मिलीसेकंड से कम होता है।

चींटियों प्रदर्शन प्रोफाइलर का उपयोग करते हुए यह समय की थोक में कुछ अलग बातें कर खर्च किया जा रहा है की तरह लग रहा:

बराबर विधि 50 लाख बार बुलाया जा रहा है।

क्या किसी को पता है कि इसे 50 मिलियन बार कॉल करने की आवश्यकता क्यों होगी और मैं इसके प्रदर्शन को कैसे बढ़ा सकता हूं?

+0

क्या आप वास्तव में देखना चाहते थे कि आपके कथन से कौन सी क्वेरी उत्पन्न की जा रही है या क्या आप मानते हैं कि यह इष्टतम क्वेरी है? –

+1

ईएफ प्रोफाइलर को आज़माएं। –

+1

समस्या यह नहीं है कि मैंने कहा है। मैंने सटीक क्वेरी ली है कि ईएफ उत्पन्न कर रहा है और इसे नियमित रूप से ADO.net का उपयोग करके एक SQL डेटा एडाप्टर में उपयोग किया जाता है, वही ऑब्जेक्ट मैन्युअल रूप से लोड करता है। यह एक सेकंड से भी कम समय में चलता है। – Dismissile

उत्तर

20

बराबर 50 एम बार क्यों कहा जाता है?

यह काफी संदिग्ध लगता है। आपके पास 10.000 समीक्षा और 50.000.000 कॉल Equals पर हैं। मान लीजिए कि यह आंतरिक रूप से ईएफ द्वारा कार्यान्वित पहचान मानचित्र के कारण होता है। पहचान मानचित्र यह सुनिश्चित करता है कि अनन्य कुंजी वाली प्रत्येक इकाई को केवल एक बार संदर्भ द्वारा ट्रैक किया जाता है, इसलिए यदि संदर्भ में पहले से ही एक ही कुंजी के साथ डेटाबेस से लोड रिकॉर्ड के रूप में उदाहरण है, तो यह नए उदाहरण को पूरा नहीं करेगा और इसके बजाय मौजूदा का उपयोग करेगा। अब यह उन संख्याओं के साथ कैसे मिल सकता है? मेरा डरावना अनुमान:

============================================= 
1st  record read | 0  comparisons 
2nd  record read | 1  comparison 
3rd  record read | 2  comparisons 
... 
10.000th record read | 9.999 comparisons 

इसका मतलब है कि प्रत्येक नए रिकॉर्ड की पहचान पहचान मानचित्र में हर मौजूदा रिकॉर्ड से की जाती है। गणित को लागू करने के सभी तुलना हम कुछ "अंकगणित अनुक्रम" कहा जाता है का उपयोग कर सकते की राशि की गणना करने के द्वारा:

a(n) = a(n-1) + 1 
Sum(n) = (n/2) * (a(1) + a(n)) 
Sum(10.000) = 5.000 * (0 + 9.999) => 5.000 * 10.000 = 50.000.000 

मुझे आशा है कि मैं अपने मान्यताओं या गणना में गलती नहीं की। रुकिए! मुझे आशा है कि मैंने गलती की क्योंकि यह अच्छा प्रतीत नहीं होता है।

परिवर्तन ट्रैकिंग बंद करने का प्रयास करें = उम्मीद है कि पहचान मानचित्र जांच बंद कर दें।

यह मुश्किल हो सकता है।से शुरू करें:

var bookAndReviews = db.Books.Where(b => b.BookId == id) 
          .Include(b => b.Reviews) 
          .AsNoTracking() 
          .FirstOrDefault(); 

लेकिन वहाँ एक बड़ा मौका है कि अपने नेविगेशन संपत्ति आबादी नहीं किया जाएगा (क्योंकि यह परिवर्तन ट्रैकिंग द्वारा नियंत्रित किया जाता है)। ऐसे मामले में इस दृष्टिकोण का उपयोग:

var book = db.Books.Where(b => b.BookId == id).AsNoTracking().FirstOrDefault(); 
book.Reviews = db.Reviews.Where(r => r.BookId == id).AsNoTracking().ToList(); 

वैसे भी आप देख सकते हैं ऑब्जेक्ट प्रकार के बराबर है पारित हो जाता है? मुझे लगता है कि इसे केवल प्राथमिक कुंजी की तुलना करनी चाहिए और यहां तक ​​कि 50 एम पूर्णांक तुलना भी ऐसी समस्या नहीं होनी चाहिए।

एक साइड नोट के रूप में ईएफ धीमा है - यह अच्छी तरह से ज्ञात तथ्य है। यह संस्थाओं को भौतिक बनाने के दौरान आंतरिक रूप से प्रतिबिंब का भी उपयोग करता है। 10.000 रिकॉर्ड "कुछ समय" ले सकते हैं। जब तक कि आप पहले से ही ऐसा नहीं कर लेते हैं, आप गतिशील प्रॉक्सी निर्माण (db.Configuration.ProxyCreationEnabled) को भी बंद कर सकते हैं।

+0

बहुत बढ़िया विश्लेषण! परीक्षणों के अनुसार (कोई एनवी गुणों वाली सरल इकाई) मैंने कुछ समय पहले बनाया है, 'AsNoTracking' भौतिकरण के लिए 50% तक कटौती करता है। मैं कल्पना कर सकता था कि ट्रैक के रूप में लोड की गई इकाइयों के लिए स्नैपशॉट निर्माण पहचान मानचित्र में 'समान' को कॉल करने से अधिक महंगा है। यदि आप एक ही संदर्भ में दूसरी बार एक ही क्वेरी (ट्रैक किए गए दोनों) को कॉल करते हैं तो यह तेजी से (पहले कॉल के 1/10 से कम) लौटाता है, ट्रैकिंग के बिना लोड करने से बहुत तेज - जो मुझे लगता है कि 'बराबर' चेक पहचान मानचित्र में अपेक्षाकृत सस्ते है। – Slauma

+0

बीटीडब्ल्यू: 'AsNoTracking() 'के साथ' शामिल 'कार्य भी करता है, नेविगेशन संग्रह पॉप्युलेट हो जाता है। (या आप का मतलब है कि रिवर्स नेविगेशन प्रॉपर्टी 'Review.Book' पॉप्युलेट नहीं की जाएगी?) – Slauma

1

मैं इस लंगड़ा लगता है, लेकिन आप दूसरी तरह के आसपास की कोशिश की है, जैसे:

var reviewsAndBooks = db.Reviews.Where(r => r.Book.BookId == id) 
         .Include(r => r.Book); 

मैं एफई से कभी कभी बेहतर प्रदर्शन पर ध्यान दिया है जब आप अपने प्रश्नों इस तरह के दृष्टिकोण (लेकिन मुझे नहीं पड़ा है क्यों पता लगाने का समय)।

+0

मैं मुद्दों के कारण व्यक्तिगत रूप से इससे बचूंगा deadlocks के साथ। – Skarsnik