2011年4月22日金曜日

PythonからPostgreSQLに大量データのinsert(その2)

以前、PythonのPostgreSQL用のドライバがうまくインストール出来ないと記載しましたが、python-develが入って無かっただけっぽいです。

yum install python-devel

で、
① ocpgdb1.0.3
② psycopg2-2.4
③ PyGreSQL-4.0
④ pg8000-1.08
の全てのドライバがinstall可能でした。
上記はPython2.6及び、2.7で確認しましたが、2.7の場合は④のインストールがいじらないとうまくいきませんでした。(詳細はここに記載されています)

で、今回の趣旨はPythonからのBulkInsertを試してみようというものです。
driverですが、前回は④を使いましたが、今回は②psycopg2を使ってみました。
事前に、
CREATE TABLE hash ( id text PRIMARY KEY , value text );
でhashテーブルというのを作っています。

ソースは以下みたいな感じです。

------ bulk insertする実行例 ------
#!/usr/bin/env python
import csv
import psycopg2
import time

if __name__ == '__main__':
    start = time.time()
    c = psycopg2.connect(host="localhost",user="postgres",password="xxxx",database="testdb")
    i = 0
    l = []
    cur = c.cursor()
    reader = csv.reader(open("./neta.csv"))
    for row in reader:
        id = row[0]
        value = row[1]
        t = (id,value)
        l.append(t)
        i += 1
        if i % 10000 == 0:
               cur.executemany("insert into hash values(%s,%s)",l)
               c.commit()
               cur = c.cursor()
               l = []
        c.commit()
    c.close()
    print time.time()-start

上記を実行すると100万件のinsertに大体200sかかりました。

因みに1件ずつinsertするのは以下のような感じです。

------ bulk insertしない実行例 ------
#!/usr/bin/env python
import csv
import psycopg2
import time

if __name__ == '__main__':
    start = time.time()
    c = psycopg2.connect(host="localhost",user="postgres",password="xxxx",database="testdb")
    i = 0
    reader = csv.reader(open("./neta.csv"))
    for row in reader:
        id = row[0]
        value = row[1]
        t = (id,value)
        cur = c.cursor()
        cur.execute("insert into hash values(%s,%s)",t)
        i += 1
        if i % 10000 == 0:
              c.commit()
    c.commit()
    c.close()
    print time.time()-start

これを実行すると100万件のinsertに220sかかりました。
executemany()を使用してもパフォーマンス改善にはさしてつながっていません。しかし、言い方を変えると20sも改善しています。これは通信のオーバヘッド以上に処理に時間がかかっている為、改善効果が大きくは見えないという事でしょう。

前回のpg8000で実施したテストよりもだいぶ高速だった為、pg8000でももう一度試してみました。上記のソースで、
・import psycopg2のところをimport pg8000とする
・psycopg2.connect のところを pg8000.dbapi.connectとする
事でつながるようになります。結果として以前やった結果と変わらずにbulkinsertを使おうが使うまいが、1000s以上かかってしまいます。理由は全く不明ですが、バインド変数とかをpg8000だとうまく使えて無いのかな・・・?

<まとめ>
・fedoraにpostgresqlのドライバをinstallする際はpython-develを入れるのを忘れずに
・psycopg2でbulkinsertの効果を確認したが、さしたる改善効果は無かった。
・pg8000とpsycopg2で大量insertのパフォーマンスを比較すると、psycopg2の方が圧倒的に早い。