2011-12-26 7 views
98

मैं 1000 सम्मिलित बयान का उपयोग कर के बीच एक प्रदर्शन की तुलना चल रहा हूँ: 1000 मूल्यों के साथ एक सम्मिलित करें कथन का उपयोगएकाधिक सम्मिलित बयानों से अधिक मान के साथ एकल सम्मिलित बनाम

INSERT INTO T_TESTS (TestId, FirstName, LastName, Age) 
    VALUES ('6f3f7257-a3d8-4a78-b2e1-c9b767cfe1c1', 'First 0', 'Last 0', 0) 
INSERT INTO T_TESTS (TestId, FirstName, LastName, Age) 
    VALUES ('32023304-2e55-4768-8e52-1ba589b82c8b', 'First 1', 'Last 1', 1) 
... 
INSERT INTO T_TESTS (TestId, FirstName, LastName, Age) 
    VALUES ('f34d95a7-90b1-4558-be10-6ceacd53e4c4', 'First 999', 'Last 999', 999) 

..versus:

INSERT INTO T_TESTS (TestId, FirstName, LastName, Age) 
VALUES 
('db72b358-e9b5-4101-8d11-7d7ea3a0ae7d', 'First 0', 'Last 0', 0), 
('6a4874ab-b6a3-4aa4-8ed4-a167ab21dd3d', 'First 1', 'Last 1', 1), 
... 
('9d7f2a58-7e57-4ed4-ba54-5e9e335fb56c', 'First 999', 'Last 999', 999) 

मेरे बड़े आश्चर्य के लिए, परिणाम मैंने जो सोचा था उसके विपरीत हैं:

  • 1000 INSERT कथन: 2 9 0 एमसीईसी।
  • 1000 मूल्यों के साथ 1 INSERT कथन: 2800 msec।

परीक्षण एसक्यूएल सर्वर प्रोफाइलर साथ MSSQL प्रबंधन स्टूडियो में सीधे निष्पादित किया जाता है माप के लिए इस्तेमाल किया (और मैं SqlClient, जो और भी अधिक है आश्चर्य की बात सब दाल परतों roundtrips पर विचार का उपयोग कर सी # कोड से यह चल रहा है इसी तरह के परिणाम मिल गया है)

क्या यह उचित या किसी तरह समझाया जा सकता है? कैसे आते हैं, एक अनुमानित तेज़ विधि 10 गुना (!) बदतर प्रदर्शन?

धन्यवाद।

संपादित करें: अटैच किया जा रहा दोनों के लिए निष्पादन की योजना: Exec Plans

+1

इन कर रहे हैं स्वच्छ परीक्षण, कुछ भी समानांतर में निष्पादित हो रहा है उपयोग कर रहा हूँ , सरल कैशिंग से बचने के लिए, कोई भी दोहराया डेटा नहीं है (प्रत्येक क्वेरी अलग-अलग डेटा के साथ है) – Borka

+1

क्या कोई ट्रिगर्स शामिल हैं? –

+1

मैंने मूल्यों पर 1000 सीमा पार करने के लिए एक कार्यक्रम को टीवीपी में परिवर्तित कर दिया और एक बड़ा प्रदर्शन लाभ प्राप्त किया। मैं एक तुलना चलाऊंगा। – Paparazzi

उत्तर

107

अलावा: SQL सर्वर 2012 इस क्षेत्र में कुछ बेहतर प्रदर्शन से पता चलता है, लेकिन नीचे नोट विशिष्ट मुद्दों से निपटने के लिए प्रतीत नहीं होता। apparently be fixed अगले बड़े संस्करण में SQL सर्वर 2012 के बाद होना चाहिए!

आपकी योजना से पता चलता है कि एकल आवेषण पैरामीटर प्रक्रियाओं (संभवतः ऑटो पैरामीटरयुक्त) का उपयोग कर रहे हैं, इसलिए इनके लिए पार्स/संकलन समय न्यूनतम होना चाहिए।

मैंने सोचा कि मैं इसे थोड़ा और अधिक देखूंगा, हालांकि एक लूप (script) स्थापित करें और VALUES खंडों की संख्या समायोजित करने और संकलन समय रिकॉर्ड करने का प्रयास किया।

मैंने प्रति पंक्ति औसत संकलन समय प्राप्त करने के लिए पंक्तियों की संख्या से संकलन समय को विभाजित किया। परिणाम के नीचे

Graph

हैं ऊपर तक 250 VALUES खंड पेश संकलन समय/खंड की संख्या एक मामूली वृद्धि की प्रवृत्ति लेकिन बहुत नाटकीय नहीं है।

Graph

लेकिन फिर वहाँ अचानक बदलाव है।

डेटा का वह अनुभाग नीचे दिखाया गया है।

+------+----------------+-------------+---------------+---------------+ 
| Rows | CachedPlanSize | CompileTime | CompileMemory | Duration/Rows | 
+------+----------------+-------------+---------------+---------------+ 
| 245 |   528 |   41 |   2400 | 0.167346939 | 
| 246 |   528 |   40 |   2416 | 0.162601626 | 
| 247 |   528 |   38 |   2416 | 0.153846154 | 
| 248 |   528 |   39 |   2432 | 0.157258065 | 
| 249 |   528 |   39 |   2432 | 0.156626506 | 
| 250 |   528 |   40 |   2448 | 0.16   | 
| 251 |   400 |   273 |   3488 | 1.087649402 | 
| 252 |   400 |   274 |   3496 | 1.087301587 | 
| 253 |   400 |   282 |   3520 | 1.114624506 | 
| 254 |   408 |   279 |   3544 | 1.098425197 | 
| 255 |   408 |   290 |   3552 | 1.137254902 | 
+------+----------------+-------------+---------------+---------------+ 

कैश्ड योजना आकार जो रैखिक अचानक से बढ़ गया था चला जाता है लेकिन CompileTime 7 गुना बढ़ जाती है और CompileMemory तक चढ़ जाता है। यह एक गैर पैरामीट्रिज्ड एक ऑटो पैरामीट्रिज्ड (1,000 पैरामीटर के साथ) होने के बीच कट ऑफ पॉइंट है। उसके बाद यह रैखिक रूप से कम कुशल लगता है (किसी दिए गए समय में संसाधित मूल्य खंडों की संख्या के मामले में)।

यह सुनिश्चित नहीं है कि यह क्यों होना चाहिए। संभवतः जब यह विशिष्ट शाब्दिक मूल्यों के लिए एक योजना संकलित कर रहा है तो उसे कुछ गतिविधि करना चाहिए जो रैखिक रूप से स्केल नहीं करता है (जैसे सॉर्टिंग)।

कैश किए गए क्वेरी प्लान के आकार को प्रभावित नहीं करता है जब मैंने पूरी तरह से डुप्लिकेट पंक्तियों वाली क्वेरी की कोशिश की और न ही स्थिरांक की तालिका के आउटपुट के क्रम को प्रभावित करता है (और जैसा कि आप एक में सम्मिलित हैं छेड़छाड़ करने वाले ढेर समय वैसे भी व्यर्थ होंगे, भले ही यह किया गया हो)।

इसके अलावा यदि तालिका में क्लस्टर्ड इंडेक्स जोड़ा जाता है तो योजना अभी भी एक स्पष्ट प्रकार का चरण दिखाती है, इसलिए यह रन टाइम पर एक प्रकार से बचने के लिए संकलित समय पर सॉर्टिंग प्रतीत नहीं होता है।

Plan

मैं एक डिबगर में इसे देखो की कोशिश की लेकिन एसक्यूएल सर्वर 2008 के अपने संस्करण के लिए सार्वजनिक प्रतीकों उपलब्ध होने की नहीं है इसलिए बजाय मैं एसक्यूएल सर्वर में बराबर UNION ALL निर्माण को देखने के लिए किया था 2005

एक ठेठ स्टैक ट्रेस यह समय की तुलना तार का एक बहुत खर्च करने के लिए प्रकट होता है नीचे

sqlservr.exe!FastDBCSToUnicode() + 0xac bytes 
sqlservr.exe!nls_sqlhilo() + 0x35 bytes  
sqlservr.exe!CXVariant::CmpCompareStr() + 0x2b bytes 
sqlservr.exe!CXVariantPerformCompare<167,167>::Compare() + 0x18 bytes 
sqlservr.exe!CXVariant::CmpCompare() + 0x11f67d bytes 
sqlservr.exe!CConstraintItvl::PcnstrItvlUnion() + 0xe2 bytes 
sqlservr.exe!CConstraintProp::PcnstrUnion() + 0x35e bytes 
sqlservr.exe!CLogOp_BaseSetOp::PcnstrDerive() + 0x11a bytes  
sqlservr.exe!CLogOpArg::PcnstrDeriveHandler() + 0x18f bytes  
sqlservr.exe!CLogOpArg::DeriveGroupProperties() + 0xa9 bytes 
sqlservr.exe!COpArg::DeriveNormalizedGroupProperties() + 0x40 bytes  
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x18a bytes 
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes 
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes 
sqlservr.exe!COptExpr::DeriveGroupProperties() + 0x146 bytes 
sqlservr.exe!CQuery::PqoBuild() + 0x3cb bytes 
sqlservr.exe!CStmtQuery::InitQuery() + 0x167 bytes 
sqlservr.exe!CStmtDML::InitNormal() + 0xf0 bytes 
sqlservr.exe!CStmtDML::Init() + 0x1b bytes 
sqlservr.exe!CCompPlan::FCompileStep() + 0x176 bytes 
sqlservr.exe!CSQLSource::FCompile() + 0x741 bytes 
sqlservr.exe!CSQLSource::FCompWrapper() + 0x922be bytes  
sqlservr.exe!CSQLSource::Transform() + 0x120431 bytes 
sqlservr.exe!CSQLSource::Compile() + 0x2ff bytes 

तो पता लगा ढेर में नामों बंद जा रहा है।

This KB article इंगित करता है कि DeriveNormalizedGroupProperties क्या क्वेरी प्रसंस्करण के normalization चरण

इस चरण में अब बंधन या algebrizing कहा जाता है और यह पिछले पार्स चरण से अभिव्यक्ति पार्स पेड़ उत्पादन लेता है और एक आउटपुट कहा जाता था साथ जुड़ा हुआ है अनुकूलन (आगे इस मामले में मामूली योजना अनुकूलन) [ref] पर जाने के लिए बीजगणित अभिव्यक्ति वृक्ष (क्वेरी प्रोसेसर पेड़)।

मैंने एक और प्रयोग (Script) की कोशिश की जो मूल परीक्षण को फिर से चलाने के लिए था लेकिन तीन अलग-अलग मामलों को देख रहा था।

  1. पहला नाम और अंतिम नाम लंबाई 10 वर्णों की स्ट्रिंग्स जिनमें कोई डुप्लीकेट नहीं है।
  2. पहला नाम और अंतिम नाम लम्बाई वाले 50 वर्णों की स्ट्रिंग्स जिनमें कोई डुप्लिकेट नहीं है।
  3. पहला नाम और अंतिम नाम सभी डुप्लीकेट के साथ लंबाई 10 वर्णों की स्ट्रिंग्स।

Graph

यह स्पष्ट रूप से देखा जा सकता है तार बदतर बातें लंबे समय तक कि और कहा कि इसके विपरीत अधिक डुप्लिकेट बेहतर चीजों मिलता है। जैसा कि पहले उल्लिखित डुप्लीकेट कैश किए गए योजना आकार को प्रभावित नहीं करते हैं, इसलिए मुझे लगता है कि बीजगणित अभिव्यक्ति वृक्ष का निर्माण करते समय डुप्लिकेट पहचान की प्रक्रिया होनी चाहिए।

संपादित

एक जगह है जहाँ इस जानकारी का लाभ उठाया है shown by @Lieven here

SELECT * 
FROM (VALUES ('Lieven1', 1), 
      ('Lieven2', 2), 
      ('Lieven3', 3))Test (name, ID) 
ORDER BY name, 1/ (ID - ID) 

क्योंकि संकलन समय पर यह तय कर सकते हैं कि Name स्तंभ कोई डुप्लिकेट इसे माध्यमिक 1/ (ID - ID) अभिव्यक्ति द्वारा आदेश देने को छोड़ देता है है रन टाइम (योजना में क्रमबद्ध केवल एक ORDER BY कॉलम है) और शून्य त्रुटि से विभाजित नहीं किया जाता है। यदि तालिका में डुप्लिकेट जोड़े जाते हैं तो सॉर्ट ऑपरेटर कॉलम द्वारा दो ऑर्डर दिखाता है और अपेक्षित त्रुटि उठाई जाती है।

+5

आपके पास जादू संख्या संख्याऑफ्रो/कॉलमकाउंट = 250 है। अपनी क्वेरी को केवल तीन कॉलम का उपयोग करने के लिए बदलें और परिवर्तन 333 पर होगा। जादू संख्या 1000 कैश किए गए योजना में उपयोग किए गए पैरामीटर की अधिकतम संख्या की तरह कुछ हो सकता है।यह < 'सूची के साथ '<पैरामीटर सूची>' के साथ एक योजना उत्पन्न करने के लिए" आसान "होना है। –

+1

@MikaelEriksson - सहमत हुए। 1000 मूल्य वाले 250 पंक्ति वाले को ऑटो पैरामीटर बनाया गया है जो 251 पंक्ति नहीं करता है, जो कि अंतर में प्रतीत होता है। यकीन नहीं है कि क्यों। शायद यह शाब्दिक मूल्यों को डुप्लिकेट या कुछ होने पर ढूंढने में समय बिताता है। –

+1

यह एक बहुत पागल मुद्दा है, मैं बस इसके द्वारा दुखी हो गया। यह एक अच्छा जवाब है धन्यवाद –

21

यह बहुत आश्चर्य की बात नहीं है: छोटे डालने के लिये कार्य योजना एक बार गणना की, और उसके बाद 1000 बार पुन: उपयोग किया जाता है। योजना को पार्स करना और तैयार करना त्वरित है, क्योंकि इसमें डेल के साथ केवल चार मान हैं। दूसरी ओर, 1000-पंक्ति योजना को 4000 मानों (या 4000 पैरामीटर से निपटने की आवश्यकता है यदि आपने अपने सी # परीक्षणों को पैरामीटर किया है)। एसक्यूएल सर्वर पर 99 9 राउंडट्रिप्स को समाप्त करके यह आसानी से प्राप्त होने वाली समय बचत को खा सकता है, खासकर यदि आपका नेटवर्क अत्यधिक धीमा नहीं है।

9

समस्या को संकलित करने के लिए शायद उस समय के साथ समस्या का सामना करना पड़ता है।

आप आवेषण, तुम सच में क्या करने की जरूरत तेजी लाने के लिए चाहते हैं, तो उन्हें एक सौदे में लपेट है:

BEGIN TRAN; 
INSERT INTO T_TESTS (TestId, FirstName, LastName, Age) 
    VALUES ('6f3f7257-a3d8-4a78-b2e1-c9b767cfe1c1', 'First 0', 'Last 0', 0); 
INSERT INTO T_TESTS (TestId, FirstName, LastName, Age) 
    VALUES ('32023304-2e55-4768-8e52-1ba589b82c8b', 'First 1', 'Last 1', 1); 
... 
INSERT INTO T_TESTS (TestId, FirstName, LastName, Age) 
    VALUES ('f34d95a7-90b1-4558-be10-6ceacd53e4c4', 'First 999', 'Last 999', 999); 
COMMIT TRAN; 

सी # से, आपके पास एक मेज महत्वपूर्ण पैरामीटर का उपयोग करने पर विचार करें। अर्धविराम से अलग करके, एक बैच में एकाधिक कमांड जारी करना, एक और तरीका है जो मदद करेगा।

+1

पुन: "जारी करना एक बैच में एकाधिक कमांड ": जो थोड़ा सा मदद करता है, लेकिन बहुत कुछ नहीं। लेकिन मैं निश्चित रूप से ट्रांसलेशन में लपेटने के अन्य दो विकल्पों से सहमत हूं (क्या ट्रान्स वास्तव में काम करते हैं या इसे सिर्फ ट्रैन होना चाहिए?) या टीवीपी का उपयोग करना। –

1

मैं एक सी ++ प्रोग्राम (एमएफसी/ओडीबीसी) के साथ कई 100k पंक्तियों के साथ एक तालिका को बदलने की कोशिश कर रहा एक समान स्थिति में भाग गया।

चूंकि इस ऑपरेशन में बहुत लंबा समय लगा, इसलिए मुझे एक में कई प्रविष्टियां बंडल हुईं (MSSQL limitations के कारण 1000 तक)। मेरा अनुमान है कि बहुत से एकल डालने वाले बयान here के वर्णन के समान ओवरहेड बनाएंगे।

हालांकि, यह पता चला है कि वास्तव में रूपांतरण ले लिया काफी लंबे समय तक सा:

 Method 1  Method 2  Method 3 
     Single Insert Multi Insert Joined Inserts 
Rows 1000   1000   1000 
Insert 390 ms   765 ms  270 ms 
per Row 0.390 ms  0.765 ms  0.27 ms 

तो, CDatabase को 1000 एकल कॉल :: एक भी सम्मिलित बयान के साथ ExecuteSql प्रत्येक (विधि 1) मोटे तौर पर दो बार के रूप हैं 1000 मान tuples (विधि 2) के साथ एक बहु लाइन INSERT कथन के साथ सीडी डाटाबेस :: ExecuteSql के लिए एक कॉल के रूप में तेज़।

अद्यतन: तो, अगली चीज़ मैंने कोशिश की थी कि एक अलग स्ट्रिंग में 1000 अलग INSERT कथन बंडल करें और सर्वर को निष्पादित करें (विधि 3)। यह पता चला यह भी एक सा विधि की तुलना में तेजी से होता है 1.

संपादित करें: मैं Microsoft SQL सर्वर एक्सप्रेस संस्करण (64-बिट) v10.0.2531.0

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