Postgres में बल्क इंसर्ट करने का सबसे तेज़ तरीका क्या है?


242

मुझे प्रोग्रामर के एक डेटाबेस में 10 लाख रिकॉर्ड दर्ज करने की आवश्यकता है। वर्तमान में मैं एक एकल "क्वेरी" में 1000 कथन सम्मिलित कर रहा हूं।

वहाँ यह करने के लिए एक बेहतर तरीका है, कुछ थोक डालने बयान मैं के बारे में पता नहीं है?

जवाबों:


211

PostgreSQL के पास शुरू में एक डेटाबेस को सर्वश्रेष्ठ रूप से आबाद करने के बारे में एक गाइड है , और वे थोक लोडिंग पंक्तियों के लिए COPY कमांड का उपयोग करने का सुझाव देते हैं। गाइड के पास प्रक्रिया को तेज करने के कुछ अन्य अच्छे सुझाव हैं, जैसे डेटा लोड करने से पहले अनुक्रमित और विदेशी कुंजियों को निकालना (और बाद में उन्हें वापस जोड़ना)।


33
मैंने stackoverflow.com/questions/12206600/… में भी विस्तृत करने के लिए थोड़ा और विस्तार से लिखा ।
क्रेग रिंगर

24
); @CraigRinger वाह, "थोड़ा और अधिक विस्तार" सबसे अच्छा ख़ामोश मैंने पूरा सप्ताह देखा है
culix

इंस्टाल-पैकेज NpgsqlBulkCopy
Elyor

1
-Since इंडेक्स का उपयोग db रिकॉर्ड्स के भौतिक लेआउट के लिए भी किया जाता है। सुनिश्चित नहीं है कि किसी डेटाबेस में अनुक्रमणिका को हटाना एक अच्छा विचार है।
फराज

लेकिन आपकी सिफारिश, मेमोरी में कुछ भी नहीं !!! और अगर आपके बैच का आकार छोटी संख्या में हो सकता है, तो बहुत बुरा काम किया है यह कक्षा है :( मैं npgsql CopyIn वर्ग की कोशिश करता हूं, क्योंकि यह CSV के रूप में है जैसे कि PG क्वेरी स्टेटमेंट में मैपिंग की गई है। आप बिग टेबल के लिए प्रयास कर सकते हैं?
EFor

93

COPY का उपयोग करने के लिए एक विकल्प है, जो मल्टीग्रो वैल्यू सिंटैक्स है जो पोस्टग्रैज का समर्थन करता है। से प्रलेखन :

INSERT INTO films (code, title, did, date_prod, kind) VALUES
    ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
    ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');

उपरोक्त कोड दो पंक्तियों को सम्मिलित करता है, लेकिन आप इसे मनमाने ढंग से बढ़ा सकते हैं, जब तक कि आप तैयार स्टेटमेंट टोकन की अधिकतम संख्या को हिट न करें (यह $ 999 हो सकता है, लेकिन मैं इसके बारे में 100% निश्चित नहीं हूं)। कभी-कभी कोई COPY का उपयोग नहीं कर सकता है, और यह उन स्थितियों के लिए एक योग्य प्रतिस्थापन है।


12
क्या आप जानते हैं कि इस पद्धति का प्रदर्शन COPY से कैसे तुलना करता है?
हम्फ्रीज दें

यदि आप एक अनुमतियों की समस्या में भाग लेते हैं, तो इसे आज़माने से पहले, COPY का उपयोग करें ... FROM STDIN
एंड्रयू स्कॉट इवांस

यदि आप पंक्ति-स्तरीय सुरक्षा का उपयोग कर रहे हैं, तो यह सबसे अच्छा है जो आप कर सकते हैं। "COPY FROM को पंक्ति-स्तरीय सुरक्षा वाली तालिकाओं के लिए समर्थित नहीं है" संस्करण 12 के रूप में
Eloff

COPY विस्तारित INSERT
हिपर्ट्रैकर

24

चीजों को गति देने का एक तरीका स्पष्ट रूप से कई आवेषण करना है या एक लेनदेन के भीतर कॉपी करना है (1000 का कहना है)। पोस्टग्रेज का डिफ़ॉल्ट व्यवहार प्रत्येक कथन के बाद करना है, इसलिए कमिट्स को बैच कर, आप कुछ ओवरहेड से बच सकते हैं। जैसा कि डैनियल के उत्तर में गाइड कहता है, आपको काम करने के लिए ऑटोकॉमिट को निष्क्रिय करना पड़ सकता है। नीचे दिए गए टिप्पणी पर भी ध्यान दें जो कि Wal_buffers के आकार को 16 MB तक बढ़ाने का सुझाव देता है।


1
यह ध्यान देने योग्य है कि आपके द्वारा एक ही लेन-देन में कितने आवेषण / प्रतियों की सीमा आप जितना भी प्रयास कर सकते हैं, उससे कहीं अधिक होने की संभावना है। आप एक ही लेन-देन के भीतर लाखों और लाखों पंक्तियों को जोड़ सकते हैं और समस्याओं में नहीं चल सकते।
सुमीत जैन

@SumeetJain हाँ, मैं प्रति लेनदेन / प्रतियों की संख्या के संदर्भ में स्पीड 'स्वीट स्पॉट' पर टिप्पणी कर रहा हूं।
दानी द साने

क्या लेन-देन चलने के दौरान यह टेबल लॉक हो जाएगा?
लैम्ब्डा फेयरी

15

UNNESTसरणियों के साथ फ़ंक्शन का उपयोग मल्टीरो वैल्यू सिंटैक्स के साथ किया जा सकता है। मुझे लगता है कि इस पद्धति का उपयोग की तुलना में धीमी कर रहा हूँ COPY, लेकिन यह psycopg और अजगर के साथ काम करने में मेरे लिए उपयोगी है (अजगर listपारित कर दिया करने के लिए cursor.executeस्नातकोत्तर हो जाता है ARRAY):

INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
    UNNEST(ARRAY[1, 2, 3]), 
    UNNEST(ARRAY[100, 200, 300]), 
    UNNEST(ARRAY['a', 'b', 'c'])
);

VALUESअतिरिक्त अस्तित्व की जाँच के साथ subselect का उपयोग किए बिना :

INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
SELECT * FROM (
    SELECT UNNEST(ARRAY[1, 2, 3]), 
           UNNEST(ARRAY[100, 200, 300]), 
           UNNEST(ARRAY['a', 'b', 'c'])
) AS temptable
WHERE NOT EXISTS (
    SELECT 1 FROM tablename tt
    WHERE tt.fieldname1=temptable.fieldname1
);

बल्क अपडेट के लिए समान सिंटैक्स:

UPDATE tablename
SET fieldname1=temptable.data
FROM (
    SELECT UNNEST(ARRAY[1,2]) AS id,
           UNNEST(ARRAY['a', 'b']) AS data
) AS temptable
WHERE tablename.id=temptable.id;

11

आप उपयोग कर सकते हैं COPY table TO ... WITH BINARYजो " पाठ और सीएसवी प्रारूपों की तुलना में कुछ तेज है ।" केवल तभी करें जब आपके पास सम्मिलित करने के लिए लाखों पंक्तियाँ हों, और यदि आप बाइनरी डेटा के साथ सहज हैं।

बाइनरी इनपुट के साथ psycopg2 का उपयोग करके पायथन में एक उदाहरण नुस्खा है


9

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

मेरा पहला दृष्टिकोण हमेशा है: लक्ष्य तालिका के समान संरचना वाला एक (अस्थायी) तालिका बनाएं (तालिका tmp AS चुनें * लक्ष्य से जहां 1 = 0), और फ़ाइल को अस्थायी तालिका में पढ़कर प्रारंभ करें। फिर मैं जांचता हूं कि क्या जांचा जा सकता है: डुप्लिकेट, चाबियाँ जो पहले से ही लक्ष्य में मौजूद हैं, आदि।

तो मैं बस एक "लक्ष्य में सम्मिलित करें चयन * tmp से" या इसी तरह करते हैं।

यदि यह विफल हो जाता है, या बहुत लंबा हो जाता है, तो मैं इसे निरस्त करता हूं और अन्य तरीकों (अस्थायी रूप से अनुक्रमित / बाधाएं, आदि को छोड़ने) पर विचार करता हूं।



6

मुझे बस इस मुद्दे का सामना करना पड़ा और Postgres को थोक आयात के लिए csvsql ( रिलीज ) की सिफारिश करेगा । बल्क इंसर्ट करने के लिए आप बस createdbऔर फिर उपयोग कर सकते हैं csvsql, जो आपके डेटाबेस से जुड़ता है और CSV के संपूर्ण फ़ोल्डर के लिए अलग-अलग टेबल बनाता है।

$ createdb test 
$ csvsql --db postgresql:///test --insert examples/*.csv

1
Csvsql के लिए, क्रम में भी किसी भी संभावित formating त्रुटियों से स्रोत सीएसवी साफ करने के लिए, यह सबसे अच्छा है का पालन करने के लिए इन निर्देशों का अधिक प्रलेखन, यहाँ
साल

0

बाहरी फ़ाइल सबसे अच्छा और विशिष्ट बल्क-डेटा है

"बल्क डेटा" शब्द "बहुत सारे डेटा" से संबंधित है, इसलिए मूल कच्चे डेटा का उपयोग करना स्वाभाविक है , इसे एसक्यूएल में बदलने की आवश्यकता नहीं है। "बल्क इंसर्ट" के लिए विशिष्ट कच्चे डेटा फाइलें CSV और JSON हैं फॉर्मेट हैं।

कुछ परिवर्तन के साथ थोक डालें

में ईटीएल अनुप्रयोगों और घूस प्रक्रियाओं, हम इसे डालने से पहले डेटा को बदलने की जरूरत है। अस्थायी तालिका में (बहुत अधिक) डिस्क स्थान की खपत होती है, और यह इसे करने का सबसे तेज़ तरीका नहीं है। PostgreSQL विदेशी डेटा आवरण (FDW) सबसे अच्छा विकल्प है।

CSV का उदाहरण । मान लीजिए tablename (x, y, z)एसक्यूएल और एक सीएसवी फ़ाइल की तरह

fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...

आप COPYलोड करने के लिए क्लासिक एसक्यूएल का उपयोग कर सकते हैं ( जैसा कि मूल डेटा है) tmp_tablename, वे फ़िल्टर्ड डेटा सम्मिलित करते हैं tablename... लेकिन, डिस्क की खपत से बचने के लिए, सबसे अच्छा है सीधे द्वारा निगलना

INSERT INTO tablename (x, y, z)
  SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms 
  FROM tmp_tablename_fdw
  -- WHERE condictions
;

आपको FDW के लिए डेटाबेस तैयार करने की आवश्यकता है, और इसके बजाय स्थैतिक tmp_tablename_fdwआप एक फ़ंक्शन का उपयोग कर सकते हैं जो इसे उत्पन्न करता है :

CREATE EXTENSION file_fdw;
CREATE SERVER import FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE tmp_tablename_fdw(
  ...
) SERVER import OPTIONS ( filename '/tmp/pg_io/file.csv', format 'csv');

JSON उदाहरण । दो फाइलों का एक सेट myRawData1.jsonऔर Ranger_Policies2.jsonइसके द्वारा निगला जा सकता है:

INSERT INTO tablename (fname, metadata, content)
 SELECT fname, meta, j  -- do any data transformation here
 FROM jsonb_read_files('myRawData%.json')
 -- WHERE any_condiction_here
;

जहाँ फंक्शन jsonb_read_files () मास्क द्वारा परिभाषित फ़ोल्डर की सभी फाइलों को पढ़ता है:

CREATE or replace FUNCTION jsonb_read_files(
  p_flike text, p_fpath text DEFAULT '/tmp/pg_io/'
) RETURNS TABLE (fid int,  fname text, fmeta jsonb, j jsonb) AS $f$
  WITH t AS (
     SELECT (row_number() OVER ())::int id, 
           f as fname,
           p_fpath ||'/'|| f as f
     FROM pg_ls_dir(p_fpath) t(f)
     WHERE    f like p_flike
  ) SELECT id,  fname,
         to_jsonb( pg_stat_file(f) ) || jsonb_build_object('fpath',p_fpath),
         pg_read_file(f)::jsonb
    FROM t
$f$  LANGUAGE SQL IMMUTABLE;

गज़िप स्ट्रीमिंग का अभाव

"फ़ाइल अंतर्ग्रहण" (मुख्य डेटा में मुख्य रूप से) के लिए सबसे लगातार विधि गज़िप प्रारूप पर मूल फ़ाइल को संरक्षित कर रही है और इसे स्ट्रीमिंग एल्गोरिथ्म के साथ स्थानांतरित कर रही है , कुछ भी जो तेजी से और बिना डिस्क पाइप में डिस्क खपत के बिना चल सकता है:

 gunzip remote_or_local_file.csv.gz | convert_to_sql | psql 

तो आदर्श (भविष्य) प्रारूप के लिए एक सर्वर विकल्प है .csv.gz

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.