2011-11-15 14 views
24

के साथ बाइनरी कॉपी तालिका का उपयोग करें मेरे पास multidimensional सरणी फ़ाइलों से एक PostgreSQL डेटाबेस में स्थानांतरित करने के लिए लाखों पंक्तियां हैं। मेरे उपकरण पाइथन और psycopg2 हैं। थोक इंस्टर्ट डेटा का सबसे प्रभावी तरीका copy_from का उपयोग कर रहा है। हालांकि, मेरा डेटा ज्यादातर 32-बिट फ़्लोटिंग पॉइंट नंबर (वास्तविक या फ्लोट 4) है, इसलिए मैं असली → टेक्स्ट → असली से कनवर्ट नहीं करना चाहता हूं।psycopg2

CREATE TABLE num_data 
(
    id serial PRIMARY KEY NOT NULL, 
    node integer NOT NULL, 
    ts smallint NOT NULL, 
    val1 real, 
    val2 double precision 
); 

वह स्थान है जहां मैं अजगर तार (पाठ) का उपयोग कर के साथ में हूँ:

# Just one row of data 
num_row = [23253, 342, -15.336734, 2494627.949375] 

import psycopg2 
# Python3: 
from io import StringIO 
# Python2, use: from cStringIO import StringIO 

conn = psycopg2.connect("dbname=mydb user=postgres") 
curs = conn.cursor() 

# Convert floating point numbers to text, write to COPY input 
cpy = StringIO() 
cpy.write('\t'.join([repr(x) for x in num_row]) + '\n') 

# Insert data; database converts text back to floating point numbers 
cpy.seek(0) 
curs.copy_from(cpy, 'num_data', columns=('node', 'ts', 'val1', 'val2')) 
conn.commit() 

वहाँ एक बराबर है कि एक द्विआधारी मोड का उपयोग कर काम कर सकता है यहाँ एक उदाहरण डेटाबेस DDL है? यानी, बाइनरी में फ्लोटिंग पॉइंट नंबर रखें? न केवल यह फ़्लोटिंग पॉइंट परिशुद्धता को संरक्षित करेगा, लेकिन यह तेज़ हो सकता है।

(नोट: उदाहरण के रूप में एक ही सटीक को देखने के लिए, का उपयोग SET extra_float_digits='2')

+0

ठीक है, आप [सीओपीवाई के साथ बाइनरी फाइल आयात कर सकते हैं] (http://www.postgresql.org/docs/9.1/interactive/sql-copy.html), लेकिन इसके लिए पूरी फाइल में होना चाहिए एक विशिष्ट बाइनरी प्रारूप, न केवल एक मान। –

+0

@Erwin, हाँ मैंने सीओपीवाई के लिए बाइनरी मोड के बारे में पढ़ा है, लेकिन मुझे यकीन नहीं है कि यह psycopg2 द्वारा समर्थित है, या यदि मुझे एक अलग दृष्टिकोण का उपयोग करना चाहिए। –

+0

मैंने उपयोग किए गए बाइनरी फ़ाइल प्रारूप का एकमात्र एप्लिकेशन एक फ़ाइल आयात करने के लिए है जिसे * PostgreSQL से * निर्यात किया गया था। मुझे किसी अन्य प्रोग्राम के बारे में पता नहीं है जो विशिष्ट प्रारूप लिख सकता है। इसका मतलब यह नहीं है कि यह कहीं कहीं बाहर नहीं हो सका। यदि यह दोहराए गए ऑपरेशन के लिए है, तो आप पोस्टग्रेस को एक बार टेक्स्ट फॉर्म में कॉपी कर सकते हैं, बाइनरी फाइल को लिख सकते हैं और अगली बार 'फॉर्मेट बिनरी' से कॉपी कर सकते हैं। –

उत्तर

29

यहाँ अजगर 3 के लिए से कॉपी की दोहरी बराबर है:

from io import BytesIO 
from struct import pack 
import psycopg2 

# Two rows of data; "id" is not in the upstream data source 
# Columns: node, ts, val1, val2 
data = [(23253, 342, -15.336734, 2494627.949375), 
     (23256, 348, 43.23524, 2494827.949375)] 

conn = psycopg2.connect("dbname=mydb user=postgres") 
curs = conn.cursor() 

# Determine starting value for sequence 
curs.execute("SELECT nextval('num_data_id_seq')") 
id_seq = curs.fetchone()[0] 

# Make a binary file object for COPY FROM 
cpy = BytesIO() 
# 11-byte signature, no flags, no header extension 
cpy.write(pack('!11sii', b'PGCOPY\n\377\r\n\0', 0, 0)) 

# Columns: id, node, ts, val1, val2 
# Zip: (column position, format, size) 
row_format = list(zip(range(-1, 4), 
         ('i', 'i', 'h', 'f', 'd'), 
         (4, 4, 2, 4, 8))) 
for row in data: 
    # Number of columns/fields (always 5) 
    cpy.write(pack('!h', 5)) 
    for col, fmt, size in row_format: 
     value = (id_seq if col == -1 else row[col]) 
     cpy.write(pack('!i' + fmt, size, value)) 
    id_seq += 1 # manually increment sequence outside of database 

# File trailer 
cpy.write(pack('!h', -1)) 

# Copy data to database 
cpy.seek(0) 
curs.copy_expert("COPY num_data FROM STDIN WITH BINARY", cpy) 

# Update sequence on database 
curs.execute("SELECT setval('num_data_id_seq', %s, false)", (id_seq,)) 
conn.commit() 

अद्यतन

मैं कॉपी के लिए फ़ाइलों को लिखने के लिए उपरोक्त दृष्टिकोण को फिर से लिखता हूं। पायथन में मेरा डेटा NumPy arrays में है, इसलिए इनका उपयोग करना समझ में आता है। यहाँ 1M पंक्तियां, 7 कॉलम के साथ साथ कुछ उदाहरण data है:

CREATE TABLE num_data_binary 
(
    id integer PRIMARY KEY, 
    node integer NOT NULL, 
    ts smallint NOT NULL, 
    s0 real, 
    s1 real, 
    s2 real, 
    s3 real, 
    s4 real, 
    s5 real, 
    s6 real 
) WITH (OIDS=FALSE); 

और एक अन्य समान तालिका num_data_text नामित:

import psycopg2 
import numpy as np 
from struct import pack 
from io import BytesIO 
from datetime import datetime 

conn = psycopg2.connect("dbname=mydb user=postgres") 
curs = conn.cursor() 

# NumPy record array 
shape = (7, 2000, 500) 
print('Generating data with %i rows, %i columns' % (shape[1]*shape[2], shape[0])) 

dtype = ([('id', 'i4'), ('node', 'i4'), ('ts', 'i2')] + 
     [('s' + str(x), 'f4') for x in range(shape[0])]) 
data = np.empty(shape[1]*shape[2], dtype) 
data['id'] = np.arange(shape[1]*shape[2]) + 1 
data['node'] = np.tile(np.arange(shape[1]) + 1, shape[2]) 
data['ts'] = np.repeat(np.arange(shape[2]) + 1, shape[1]) 
data['s0'] = np.random.rand(shape[1]*shape[2]) * 100 
prv = 's0' 
for nxt in data.dtype.names[4:]: 
    data[nxt] = data[prv] + np.random.rand(shape[1]*shape[2]) * 10 
    prv = nxt 

मेरी डेटाबेस पर, मैं दो तालिकाओं कि तरह लग रहे है।

def prepare_text(dat): 
    cpy = BytesIO() 
    for row in dat: 
     cpy.write('\t'.join([repr(x) for x in row]) + '\n') 
    return(cpy) 

def prepare_binary(dat): 
    pgcopy_dtype = [('num_fields','>i2')] 
    for field, dtype in dat.dtype.descr: 
     pgcopy_dtype += [(field + '_length', '>i4'), 
         (field, dtype.replace('<', '>'))] 
    pgcopy = np.empty(dat.shape, pgcopy_dtype) 
    pgcopy['num_fields'] = len(dat.dtype) 
    for i in range(len(dat.dtype)): 
     field = dat.dtype.names[i] 
     pgcopy[field + '_length'] = dat.dtype[i].alignment 
     pgcopy[field] = dat[field] 
    cpy = BytesIO() 
    cpy.write(pack('!11sii', b'PGCOPY\n\377\r\n\0', 0, 0)) 
    cpy.write(pgcopy.tostring()) # all rows 
    cpy.write(pack('!h', -1)) # file trailer 
    return(cpy) 

यह कैसे मैं करने के लिए सहायक कार्यों का उपयोग कर रहा:

यहाँ NumPy रिकॉर्ड सरणी में जानकारी का उपयोग करके कॉपी (दोनों पाठ और द्विआधारी प्रारूप) के लिए डेटा तैयार करने के लिए कुछ सरल सहायक कार्य हैं बेंचमार्क दो कॉपी प्रारूप तरीके:

Processing copy object for num_data_text 
Copy object prepared in 0:01:15.288695; 84355016 bytes; transfering to database 
Database copy time: 0:00:37.929166 
     Total time: 0:01:53.217861 
Processing copy object for num_data_binary 
Copy object prepared in 0:00:01.296143; 80000021 bytes; transfering to database 
Database copy time: 0:00:23.325952 
     Total time: 0:00:24.622095 
:

def time_pgcopy(dat, table, binary): 
    print('Processing copy object for ' + table) 
    tstart = datetime.now() 
    if binary: 
     cpy = prepare_binary(dat) 
    else: # text 
     cpy = prepare_text(dat) 
    tendw = datetime.now() 
    print('Copy object prepared in ' + str(tendw - tstart) + '; ' + 
      str(cpy.tell()) + ' bytes; transfering to database') 
    cpy.seek(0) 
    if binary: 
     curs.copy_expert('COPY ' + table + ' FROM STDIN WITH BINARY', cpy) 
    else: # text 
     curs.copy_from(cpy, table) 
    conn.commit() 
    tend = datetime.now() 
    print('Database copy time: ' + str(tend - tendw)) 
    print('  Total time: ' + str(tend - tstart)) 
    return 

time_pgcopy(data, 'num_data_text', binary=False) 
time_pgcopy(data, 'num_data_binary', binary=True) 

यहाँ पिछले दो time_pgcopy आदेशों से उत्पादन होता है

तो दोनों NumPy → फ़ाइल और फ़ाइल → डेटाबेस चरणों बाइनरी दृष्टिकोण के साथ तेजी से तेज हैं। स्पष्ट अंतर यह है कि कैसे पाइथन सीओपीवाई फ़ाइल तैयार करता है, जो टेक्स्ट के लिए वास्तव में धीमा है। आम तौर पर, बाइनरी प्रारूप इस स्कीमा के पाठ प्रारूप के रूप में उस समय के 2/3 में डेटाबेस में लोड होता है।

आखिरकार, मैंने डेटाबेस के भीतर दोनों तालिकाओं में मूल्यों की तुलना करने के लिए यह देखने के लिए तुलना की कि संख्याएं अलग हैं या नहीं। पंक्तियों में से लगभग 1.46% कॉलम s0 के लिए अलग-अलग मान हैं, और यह अंश s6 (शायद यादृच्छिक विधि से संबंधित) के लिए 6.17% तक बढ़ जाता है। सभी 70 एम 32-बिट फ्लोट मानों के बीच गैर-शून्य पूर्ण अंतर 9.3132257e-010 और 7.6293945e-006 के बीच हैं। टेक्स्ट और बाइनरी लोडिंग विधियों के बीच ये छोटे अंतर टेक्स्ट प्रारूप विधि के लिए आवश्यक फ्लोट → टेक्स्ट → फ्लोट रूपांतरण से सटीकता के नुकसान के कारण हैं।

+0

बहुत अच्छा। क्या आपने खुद को इसे कहीं से प्राप्त करने के लिए लिखा था? और क्या यह वास्तव में प्रदर्शन में सुधार करता है? –

+0

अगर यह काम करता है, तो शांत! एक तरफ प्रारूपित करें, समाधान हालांकि psycopg की 'copy_expert() 'का उपयोग करना है। – piro

+0

@Erwin, हाँ मैंने इसे [कॉपी] (http://www.postgresql.org/docs/current/interactive/sql-copy.html) के लिए उत्कृष्ट दस्तावेज़ों का उपयोग करके प्रोग्राम किया है, [struct] (http: // docs। python.org/library/struct.html) और [dtype] (http://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html)। बेंचमार्क में हैं, और अच्छे लग रहे हैं। –

1

Here मेरा संस्करण है। माइक के संस्करण के आधार पर।

इसकी बहुत तदर्थ

लेकिन दोनों पेशेवरों हैं:

  • readline
  • उदाहरण ओवरलोडिंग hstore द्विपदीय प्रारूप में लिखने के लिए कैसे द्वारा जनरेटर और एक धारा के रूप में कार्य करता है अपेक्षा करें।
संबंधित मुद्दे