2010-09-20 12 views
8

का उपयोग करके बहुत धीमी डालने की प्रक्रिया मैं सी # से एसक्यूएलसेवर 2008 एक्सप्रेस डीबी से लिंकक्टोस्क्ल का उपयोग करके बड़ी संख्या में रिकॉर्ड डाल रहा हूं। ऐसा लगता है कि इसमें सम्मिलन बहुत धीमा है। कोड स्निपेट निम्नलिखित है।लिंक से एसक्यूएल

public void InsertData(int id) 
{ 

    MyDataContext dc = new MyDataContext(); 

    List<Item> result = GetItems(id); 

    foreach (var item in result) 
    { 
    DbItem dbItem = new DbItem(){ItemNo = item.No, ItemName=item.Name}; 
    dc.Items.InsertOnSubmit(); 
    } 

    dc.SubmitChanges(); 
} 

क्या मैं कुछ गलत कर रहा हूं? या रिकॉर्ड की बड़ी संख्या डालने के लिए लिंक का उपयोग करना एक बुरा विकल्प है?

अद्यतन: सभी उत्तरों के लिए धन्यवाद। @ p.campbell: रिकॉर्ड गिनती के लिए खेद है, यह एक टाइपो था, वास्तव में यह लगभग 100000 है। रिकॉर्ड्स भी 200k तक है।

सभी सुझावों के अनुसार मैंने इस ऑपरेशन को भागों (एक आवश्यकता परिवर्तन और डिज़ाइन निर्णय) में स्थानांतरित कर दिया और छोटे हिस्सों में डेटा पुनर्प्राप्त किया और उन्हें आने पर डेटाबेस में डालने के बाद। मैंने थ्रेड ऑपरेशन में यह InsertData() विधि डाली है और अब एक ही ऑपरेशन करने के लिए 25 थ्रेड के पूल बनाने के लिए SmartThreadPool का उपयोग कर रहा है। इस परिदृश्य में मैं केवल 100 रिकॉर्ड दर्ज कर रहा हूं। अब, जब मैंने लिंक या एसक्यूएल क्वेरी के साथ यह कोशिश की तो उसने समय के मामले में कोई फर्क नहीं पड़ता।

मेरी आवश्यकता के अनुसार यह ऑपरेशन हर घंटे चलने के लिए निर्धारित है और लगभग 4k-6k उपयोगकर्ताओं के लिए रिकॉर्ड प्राप्त करता है। तो, अब मैं एक उपयोगकर्ता के रूप में प्रत्येक उपयोगकर्ता डेटा (डीबी में पुनर्प्राप्त और डालने) ऑपरेशन को पूल कर रहा हूं और एक थ्रेड को सौंपा गया हूं। अब इस पूरे प्रक्रिया में लगभग 250k रिकॉर्ड के लिए लगभग 45 मिनट लगते हैं।

क्या इस तरह के कार्य करने का कोई बेहतर तरीका है? या क्या कोई मुझे सुझाव दे सकता है कि मैं इस प्रक्रिया को कैसे सुधार सकता हूं?

+1

कितने रिकॉर्ड, और ऑपरेशन कब तक चलता है? यहां क्या डेटाटाइप का उपयोग किया जा रहा है? –

+0

1000000 से अधिक रिकॉर्ड और अधिकतर स्ट्रिंग डेटाटाइप लेकिन 10 से अधिक फ़ील्ड नहीं। – JPReddy

+1

एक मिलियन आवेषण में समय लगेगा, इससे कोई फर्क नहीं पड़ता। मुझे संदेह है कि क्या आप जेनरेट किए गए एसक्यूएल स्टेटमेंट्स की प्रतिलिपि बनाते हैं, उनमें से सभी 1 मिलियन, और उन्हें विज्ञापन-प्रसार करते हैं, कि आपको प्रबंधन स्टूडियो से अधिक लाभ नहीं मिलेगा! –

उत्तर

11

एक असामान्य

Linq या SqlCommand, neither are designed for bulk copying data into SQL में SQL में डेटा की विशाल राशि डालने के लिए

आप SqlBulkCopy class का उपयोग कर सकते हैं जो थोक लोडिंग डेटा के लिए बीसीपी उपयोगिता में एसक्यूएल में बहुत अधिक डेटा स्रोत से प्रबंधित पहुंच प्रदान करता है।

SqlBulkCopy क्लास का उपयोग केवल SQL सर्वर टेबल पर डेटा लिखने के लिए किया जा सकता है। हालांकि, डेटा स्रोत SQL सर्वर तक सीमित नहीं है; किसी भी डेटा स्रोत का उपयोग किया जा सकता है, जब तक डेटा को डेटाटेबल उदाहरण में लोड किया जा सके या आईडीटाइडर उदाहरण के साथ पढ़ा जा सके।

प्रदर्शन की तुलना

SqlBulkCopy यहाँ तक कि जब एक साधारण CSV फ़ाइल से डेटा लोड हो रहा, सबसे तेजी से दूर कर रहा है।

लिंक केवल SQL में Insert कथन का भार उत्पन्न करेगा और उन्हें आपके SQL सर्वर पर भेज देगा। यह SqlCommand के साथ विज्ञापन-प्रसार प्रश्नों का उपयोग करने से अलग नहीं है। एसकएल कॉमांड बनाम लिंक का प्रदर्शन वस्तुतः समान है।

सबूत

(SQL एक्सप्रेस 2008, नेट 4,0)

SqlBulkCopy

(डेटा लोड हो रहा सहित) CSV फ़ाइल से 100000 पंक्तियों

लोड करने के लिए SqlBulkCopy का उपयोग करना
using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=EffectCatalogue;Data Source=.\\SQLEXPRESS;")) 
{ 
    conn.Open(); 
    Stopwatch watch = Stopwatch.StartNew(); 

    string csvConnString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\data\\;Extended Properties='text;'"; 
    OleDbDataAdapter oleda = new OleDbDataAdapter("SELECT * FROM [test.csv]", csvConnString); 
    DataTable dt = new DataTable(); 
    oleda.Fill(dt); 

    using (SqlBulkCopy copy = new SqlBulkCopy(conn)) 
    { 
     copy.ColumnMappings.Add(0, 1); 
     copy.ColumnMappings.Add(1, 2); 
     copy.DestinationTableName = "dbo.Users"; 
     copy.WriteToServer(dt); 
    } 
    Console.WriteLine("SqlBulkCopy: {0}", watch.Elapsed); 
} 

SqlCommand

using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestDb;Data Source=.\\SQLEXPRESS;")) 
{ 
    conn.Open(); 
    Stopwatch watch = Stopwatch.StartNew(); 
    SqlCommand comm = new SqlCommand("INSERT INTO Users (UserName, [Password]) VALUES ('Simon', 'Password')", conn); 
    for (int i = 0; i < 100000; i++) 
    { 
     comm.ExecuteNonQuery(); 
    } 
    Console.WriteLine("SqlCommand: {0}", watch.Elapsed); 
} 

LinqToSql

using (SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=TestDb;Data Source=.\\SQLEXPRESS;")) 
{ 
    conn.Open(); 
    Stopwatch watch = Stopwatch.StartNew(); 
    EffectCatalogueDataContext db = new EffectCatalogueDataContext(conn); 
    for (int i = 0; i < 100000; i++) 
    { 
     User u = new User(); 
     u.UserName = "Simon"; 
     u.Password = "Password"; 
     db.Users.InsertOnSubmit(u); 
    } 
    db.SubmitChanges(); 
    Console.WriteLine("Linq: {0}", watch.Elapsed); 
} 

परिणाम

SqlBulkCopy: 00:00:02.90704339 
SqlCommand: 00:00:50.4230604 
Linq: 00:00:48.7702995 
+1

वह प्रदर्शन वास्तव में शानदार है। उस सुझाव के लिए धन्यवाद, मैं अब इसका उपयोग कर रहा हूं। – JPReddy

3

यदि आप डेटा का बड़ा रिकॉर्ड डाल रहे हैं तो आप BULK INSERT के साथ प्रयास कर सकते हैं।

मेरे ज्ञान के अनुसार लिंक से SQL में थोक सम्मिलन के बराबर नहीं है।

3

आपको SubmitChanges() मिल गया है जो एक बार कहा जाता है, जो अच्छा है। इसका मतलब है कि केवल एक कनेक्शन और लेनदेन का उपयोग किया जा रहा है।

इसके बजाय InsertAllOnSubmit() का उपयोग करने के लिए अपने कोड को दोबारा प्रतिक्रिया देने पर विचार करें।

List<dbItem> newItems = GetItems(id).Select(x=> new DbItem{ItemNo = x.No, 
                  ItemName=x.Name}) 
            .ToList(); 
db.InsertAllOnSubmit(newItems); 
dc.SubmitChanges(); 

आईएनएसईआरटी कथन पिछले एक के रूप में एक-एक भेजा जाता है, लेकिन शायद यह और अधिक पठनीय हो सकता है?

कुछ अन्य बातों के पूछने के लिए/पर विचार करें:

  • लक्ष्य मेज पर अनुक्रमित की स्थिति क्या है? बहुत सारे लिखने को धीमा कर देंगे। * सरल या पूर्ण वसूली मॉडल में डेटाबेस है?
  • तार भरने वाले SQL कथन को कैप्चर करें। अपने SQL सर्वर डेटाबेस के विरुद्ध एक adhoc क्वेरी में उन कथन को दोहराएं। मुझे एहसास है कि आप एसक्यूएल एक्सप्रेस का उपयोग कर रहे हैं, और संभवतः एसक्यूएल प्रोफाइलर नहीं है। context.Log = Console.Out; से output your LINQ To SQL statements to the console का उपयोग करें। हालांकि सुविधा के लिए एसक्यूएल प्रोफाइलर पसंद करते हैं।
  • क्या कैप्चर किए गए SQL कथन आपके क्लाइंट कोड के समान प्रदर्शन करते हैं? यदि ऐसा है, तो perf समस्या डेटाबेस पक्ष पर है।
+0

यह आंतरिक रूप से कैसे काम करता है? इनपुट के लिए – cjk

+0

धन्यवाद। रिफैक्टरिंग किया जाता है। ज्यादा सुधार नहींआईडी फ़ील्ड के अलावा कोई अनुक्रमणिका नहीं है जो प्राथमिक कुंजी और ऑटो जेनरेट की गई है। – JPReddy

+0

@ जेपीआरडी: अच्छी चीजें। कब्जा + adhoc'd एसक्यूएल कथन के perf देखने में रुचि होगी। –

1

यहां अपने आवेदन है, जो बेहद बेहतर बनाता है के लिए एक थोक-सम्मिलित वर्ग को जोड़ने के लिए का एक अच्छा माध्यम से गुजरने के है LINQ का उपयोग कर रिकॉर्ड्स डालने का प्रदर्शन।

(सभी स्रोत कोड प्रदान की जाती है, अपने खुद के आवेदन के लिए जोड़ा जा करने के लिए तैयार।)

http://www.mikesknowledgebase.com/pages/LINQ/InsertAndDeletes.htm

तुम बस प्रदान की कक्षा में अपने कोड में तीन परिवर्तन, और लिंक बनाने के लिए की आवश्यकता होगी। शुभकामनाएँ!

संबंधित मुद्दे