2014-12-01 5 views
5

मैं दो तालिकाओं के साथ एक MySQL डेटाबेस में उपयोगकर्ताओं का एक बहुत डालने के लिए कोशिश कर रहा हूँ:MySQL बैच डालने

पहली तालिका उपयोगकर्ता डेटा होता है। एक उदाहरण INSERT इस तरह दिखता है (id प्राथमिक कुंजी है, mail एक अद्वितीय कुंजी है):

INSERT INTO users (id, mail, name) 
VALUES (NULL, "[email protected]", "John Smith") 
ON DUPLICATE KEY UPDATE name = VALUE(name) 

दूसरी तालिका समूह उपयोगकर्ता के अंतर्गत आता है शामिल हैं। यह केवल दो विदेशी कुंजी users_id और groups_id स्टोर करता है। एक उदाहरण क्वेरी इस तरह दिखती है:

INSERT INTO users_groups (users_id, groups_id) 
VALUES (LAST_INSERT_ID(), 1) 

यह सेटअप छोटे डेटा सेट के लिए पूरी तरह से ठीक काम करता है। जब मैं बड़ी मात्रा में डेटा आयात करता हूं (> 1 एम पंक्तियां) INSERT एस धीमा हो जाता है। जाहिर है, यह बहुत अच्छा होगा एक बैच डालने करना है:

INSERT INTO users (id, mail, name) 
VALUES (NULL, "[email protected]", "John Smith"), (NULL, "[email protected].tld", "Anna Smith") 
ON DUPLICATE KEY UPDATE name = VALUE(name) 

और:

INSERT INTO users_groups (users_id, groups_id) 
VALUES (LAST_INSERT_ID(), 1), (LAST_INSERT_ID(), 4) 

निश्चित रूप से समस्या है, कि LAST_INSERT_ID() केवल एक (प्रथम) एक बैच INSERT की आईडी देता है।
तो, मुझे जो चाहिए वह "नेस्टेड" बैच INSERT है, जो आईएमओ MySQL में मौजूद नहीं है।

मैं अपने INSERT को तेज़ी से बनाने के लिए क्या कर सकता हूं?

+0

यदि आपके पास मेल कॉलम पर एक अनुक्रमणिका है उदाहरण के लिए, आप समूह तालिका को भरने के लिए बस 'डालने का चयन करें' कथन के साथ जा सकते हैं। चयन कथन में, फिर आप ईमेल के आधार पर समूह को असाइन करते समय एक केस का अभिव्यक्ति का उपयोग करेंगे। लेकिन अगर हार्वे का समाधान पुष्टि हो गया है तो यह बेहतर है। – Sebas

+0

क्या आप कुछ संदर्भ दे सकते हैं? कार्यक्रमों में इतनी बड़ी संख्या में पंक्तियों को डालने का असामान्य उपयोग केस है। यदि यह एक बार का ऑपरेशन है, तो एक लंबा निष्पादन समय एक गैर-मुद्दा हो सकता है। यदि यह एक नियमित सिंक्रनाइज़ेशन है, तो शायद आपको प्रतिकृति में देखना चाहिए, या यहां तक ​​कि अपने अनुप्रयोगों में उपयोगकर्ता तालिका साझा करना चाहिए। – RandomSeed

उत्तर

5

डिफ़ॉल्ट रूप से थोक आवेषण अनुक्रमिक ऑटो वृद्धि प्रदान करते हैं, इस ज्ञान के साथ आप अपने आवेषण जैसे कर सकते हैं;

INSERT INTO users (id, mail, name) 
VALUES (NULL, "[email protected]", "John Smith"), 
     (NULL, "[email protected]", "Anna Smith"), 
     (...) # repeat n-times 
; 

SET @LASTID=LAST_INSERT_ID() 
; 

INSERT INTO users_groups (users_id, groups_id) 
VALUES (@LASTID - n , 1), # Note n in descending sequence 
      (@LASTID - n-1, 1), 
      ... 
      (@LASTID - 1 , 1), 
      (@LASTID - 0 , 4) 
; 

थोक आवेषण और ऑटो वेतन वृद्धि के बारे में अधिक जानकारी के लिए, बनाने के महत्वपूर्ण बात http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html

पर एक नजर है सुनिश्चित करें कि innodb_autoinc_lock_mode = 1

show global variables like 'innodb_autoinc_lock_mode' 

अन्यथा LOCK TABLES

में अपने आवेषण लपेटकर पर विचार
LOCK TABLES tbl_name WRITE 
... sqls ... 
UNLOCK TABLES 
+0

क्या आप वाकई पहली जेनरेट लाइन पहले जेनरेट आईडी के अनुरूप हैं? मैं उस – Sebas

+0

@ सेबास के बारे में एक लिंक देखना चाहता हूं; पहले मान की सम्मिलित आईडी प्राप्त करने का कोई तरीका नहीं है, यही कारण है कि आपको अंतिम आईडी मिलती है और पीछे की ओर घटाती है। – harvey

+0

मेरा प्रश्न है: क्या आप सुनिश्चित हैं कि 'fLA @ bar.tld' के लिए उत्पन्न आईडी @LASTID - n' है? – Sebas

1

आप सभी को एक बार एक तालिका में जाना जाता पंक्तियों के लाखों लोगों डाल रहे हैं, के रूप में the docs से इस उद्धरण इसका सबूत, LOAD DATA INFILE उपयोग करने पर विचार के बाद से यह परिदृश्य के सिर्फ इतना है कि प्रकार में गति के लिए लक्षित है:

LOAD DATA INFILE कथन एक टेक्स्ट फ़ाइल से पंक्तियों को तालिका में बहुत तेज गति से पढ़ता है।

और Speed of INSERT Statements पर:

जब एक पाठ फ़ाइल से एक मेज लोड हो रहा है, LOAD DATA INFILE का उपयोग करें। यह INSERT कथन का उपयोग करने से आमतौर पर 20 गुना तेज है।

यह माना जा रहा है कि आपका स्रोत डेटा आ रहा है, या एक टेक्स्ट फ़ाइल के रूप में प्रदान किया जा सकता है। आप फ़ाइल में समूह आईडी के रूप में अच्छी तरह से है, तो आप कुछ इस तरह कर सकते हैं: इस दृष्टिकोण से किया जा रहा समाप्त होता है तेजी से अपने वर्तमान दृष्टिकोण से

CREATE TEMPORARY TABLE load_users_groups (
    mail VARCHAR(60), 
    name VARCHAR(60), 
    groupid INT, 
    PRIMARY KEY (mail, name) 
); 

LOAD DATA INFILE '/path/to/file.csv' 
INTO TABLE load_users_groups 
FIELDS TERMINATED BY ',' 
LINES TERMINATED BY '\n'; -- use whatever optional syntax required to parse your file 

INSERT INTO users (mail, name) 
SELECT mail, name FROM load_users_groups 
ON DUPLICATE KEY UPDATE name = VALUES(name); 

INSERT INTO users_groups (users_id, groups_id) 
SELECT users.id, load_users_groups.groupid 
FROM users JOIN load_users_groups USING (mail, name); 

DROP TEMPORARY TABLE load_users_groups; 

चाहे पर निर्भर करता है आप LOAD DATA INFILE का उपयोग कर अधिक समय की बचत है कि क्या की तुलना में आप प्रदर्शन कर खर्च करते हैं डेटा को वांछित टेबल में स्थानांतरित करने के लिए दो अतिरिक्त INSERT ... SELECT कथन। आप अस्थायी तालिका पर चाबियाँ बदलना चाह सकते हैं; मैं केवल आपके प्रश्न की सामग्री पर आधारित आपके लिए इसे बेंचमार्क नहीं कर सकता। मुझे यह जानने में दिलचस्पी होगी कि यह कैसे काम करता है, हालांकि।

प्रलेखन में Bulk Data Loading for InnoDB Tables और Bulk Data Loading for MyISAM Tables के लिए युक्तियों की एक सभ्य संख्या भी है। मैं उन्हें विस्तार से नहीं देखूंगा, कम से कम नहीं क्योंकि आपने हमें कोई डीडीएल या सर्वर जानकारी नहीं दी है, लेकिन आपको अपने या अपने समय पर एक या दूसरे को पढ़ने में मदद मिल सकती है।

-1

अच्छी तरह से, जावा के साथ यह आसान है ... आप कथन के लिए दो का उपयोग कर सकते हैं :)। एक ऐसा है जिसके लिए "वांछित बैचसाइज" पुनरावृत्तियों और दूसरा "वांछित बैचसाइज" बनाता है। मेरे लिए यह बहुत तेजी से चला गया। 140 मिनट में तैयार किए गए स्टेमेंटमेंट और ओरेकल के साथ 100 मिल रिकॉर्ड (22 फ़ील्ड वाले टेबल)।

  for (int m = 0; m < MULTIPLE_OF_BATCHSIZE; m++) { //batchSizeM * m + i => PK 
 
       statement = conn.createStatement(); 
 
       for (int i = 0; i < batchSizeM; i++) { 
 
        StringBuilder sb = new StringBuilder(); 
 
        sb.append("INSERT ... 
 
        
 
        statement.addBatch(sb.toString()); 
 
       } 
 
       statement.executeBatch(); 
 

 
       java.util.Date today = new java.util.Date(); 
 
       System.out.println(m + " --- " + new java.sql.Timestamp(today.getTime())); 
 
       conn.commit();  
 
      }

0

ने वही समस्या का सामना करना पड़ा।

MySQL वास्तव में इस उद्देश्य के लिए तालिका आईडी के बड़े बैचों को विश्वसनीय रूप से आरक्षित करने का एक तरीका प्रदान नहीं करता है। मैंने बिना किसी लाभ के शोध के लिए एक अच्छा आधा दिन बिताया। कुछ हैक्स चारों ओर तैर रहे हैं, लेकिन कुछ भी आईडी आईडी मेरे डेटा पर नहीं है।

मैंने केवल एक-एक-एक आवेषण (स्क्रू से बेहतर धीमी) के साथ उपयोगकर्ता तालिका बनाई और नई पंक्ति की आईडी को मेरे ओआरएम में वापस कर दिया। मुझे काम करने के लिए एक पंक्ति आईडी मिली, इसलिए मैं इसे फेंकने में सक्षम था और डेटा जो JSON में आयात किया जाना चाहिए जो उन्हें दोनों को एक साथ रखता है। इससे मेरे लिए थोक डालने और डेटा मिलान करने के लिए यह बहुत आसान हो गया।

बेस्ट

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