私はpsycopg2モジュールをpythonで使用してpostgresデータベースから読み取るために、100万行を超える列のすべての行に対して何らかの操作を行う必要があります。
cur.fetchall()
が失敗するか、サーバーがダウンすることを知りたいですか? (私のRAMは、すべてのデータを保持するのにそれほど大きくない可能性があるため)
q="SELECT names from myTable;"
cur.execute(q)
rows=cur.fetchall()
for row in rows:
doSomething(row)
これを行うためのよりスマートな方法は何ですか?
fetchall()
は arraysize
の制限までフェッチするため、データベースへの大規模なヒットを防ぐには、管理可能なバッチで行をフェッチするか、カーソルをステップ実行します使い果たされるまで:
row = cur.fetchone()
while row:
# do something with row
row = cur.fetchone()
Burhanが指摘したソリューションは、単一の行のみをフェッチすることにより、大規模なデータセットのメモリ使用量を削減します。
行= cursor.fetchone()
ただし、行を1つずつフェッチする際の大幅な速度低下に気づきました。私はインターネット接続を介して外部データベースにアクセスします。それが理由かもしれません。
サーバーサイドカーソルを使用して一連の行をフェッチすることが、最もパフォーマンスの高いソリューションであることが証明されました。 (alecxeの回答のように)SQLステートメントを変更できますが、psycopg2が提供する機能を使用した純粋なpythonアプローチもあります。
cursor = conn.cursor('name_of_the_new_server_side_cursor')
cursor.execute(""" SELECT * FROM table LIMIT 1000000 """)
while True:
rows = cursor.fetchmany(5000)
if not rows:
break
for row in rows:
# do something with row
pass
psycopg2 wiki でサーバー側カーソルの詳細を確認できます
サーバー側カーソル の使用を検討してください:
データベースクエリが実行されると、Psycopgカーソルは通常、バックエンドから返されたすべてのレコードをフェッチして、クライアントプロセスに転送します。クエリが大量のデータを返した場合、それに比例して大量のメモリがクライアントによって割り当てられます。
データセットがクライアント側で実際に処理するには大きすぎる場合、サーバー側カーソルを作成することが可能です。この種のカーソルを使用すると、制御された量のデータのみをクライアントに転送することができるため、メモリに完全に保持せずに大きなデータセットを調べることができます。
次に例を示します。
cursor.execute("DECLARE super_cursor BINARY CURSOR FOR SELECT names FROM myTable")
while True:
cursor.execute("FETCH 1000 FROM super_cursor")
rows = cursor.fetchall()
if not rows:
break
for row in rows:
doSomething(row)
以下は、fetchmany
管理の速度で 単純なサーバー側カーソル に使用するコードです。
原則は、Psycopg2でnamed cursorを使用し、itersize
のように一度に多くの行をロードするのに適切なfetchmany
を与えることですが、暗黙的なfetchnone()
を実行するfor rec in cursor
の単一ループ。
このコードを使用して、数十億行のテーブルから1億5,000万行のクエリを1時間と200 MBのRAMで実行します。
名前付きカーソルがインタラクティブに前後にスクロールする必要がないのに適しているかわかりませんか?私はここで間違っている可能性があります。
fetchmany
ループは退屈ですが、ここでの最善の解決策だと思います。生活を楽にするために、以下を使用できます。
from functools import partial
from itertools import chain
# from_iterable added >= python 2.7
from_iterable = chain.from_iterable
# util function
def run_and_iterate(curs, sql, parms=None, chunksize=1000):
if parms is None:
curs.execute(sql)
else:
curs.execute(sql, parms)
chunks_until_empty = iter(partial(fetchmany, chunksize), [])
return from_iterable(chunks_until_empty)
# example scenario
for row in run_and_iterate(cur, 'select * from waffles_table where num_waffles > %s', (10,)):
print 'lots of waffles: %s' % (row,)