2011年1月24日月曜日

PythonでPostgresqlに大量のデータをINSERTしてみた

Python2.xでPostgresqlに大量データのInsertを試みました。
まず、SqliteやMongodbとかでやったときとの違いとしては選択出来るDriverが多いのです。

①ocpgdb1.0.3
②psycopg2-2.3.2
③PyGreSQL4.0
④pg8000-1.08
ドライバの版数は直近のものです。どれがいい・・というのは特には無さそうです。しかし、Installしてみると、④以外は全てErrorとなりました。コンパイルでエラーになるというのが共通です。何だかなー。因みに環境はFedora13+Python2.6.4+Postgresql9.0です。

サンプルみたいなものはあんまりWeb上には無いので、Manualを読みながらやってみました。

#!/usr/bin/env python
import csv
from pg8000 import DBAPI
import time

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


本当にあってるのか自信が持てないのですが、一応上記で動作しました。
例はCSVからデータを1行ずつ読み込んで100万件Insertするというものです。
しかし、integerの行に対してInsertするとエラーとなった為、テーブルを以下のようにしてid列をTEXT型で作成しました。
create table hash2 ( id TEXT PRIMARY KEY , value TEXT );

100万行のInsertには1145sかかりました。これはSqliteやMongodbと比較するとかなり遅いです。しかし冷静になって考えると1秒で約1000件弱Insertしているので、日頃の仕事で使っているDBと比較しても大差無いのかな、というところです。

以下、補足します。
・性能があがりそうなパラメータを少し変化させたが有意な差は得られませんでした。
 (wal_buffersの値を増やす、synchronous_commitをoffにする・・・等)
・id列のPRIMERY KEYを削除してやってみましたが、やはり性能差はありませんでした。
・Python3.xにはpy-postgresqlというものがあり、ドライバの品質(ドキュメント?)的には一番良さそうな気がしました。しかし、テストアプリを作るところで挫折しました。
(Transactionの実装方式がイマイチ??です)
・DBAPI2.0ではInsertするときもCursorを使うみたいです。内部的にはPreparedStatementみたいな事もしてくれてる?かは不明です。

OSSなので当然なんですが、Pythonのドライバは有志の人が勝手に作っていて、PostgreSQLのコアメンバの人は関わっていないみたいです。コアの人たちが関わってくればもうちょい扱いやすくなるのになーと思いました。