4

1.5 G XMLファイルを取得し、データを解析して、copy_fromを使用してデータベースにフィードするPythonスクリプトを作成しました。解析されたノード1000個ごとに次の関数を呼び出します。全部で約17万のノードがあり、約30万行以上を更新します。最初は非常に速く、時間が経つにつれて徐々に遅くなります。なぜこれが起こっているのか、そしてそれを修正するために私ができることについてのアイデアはありますか?

これが、データをデータベースにフィードする関数です。

 def db_update(val_str, tbl, cols):

    conn = psycopg2.connect("dbname=<mydb> user=postgres password=<mypw>")
    cur = conn.cursor()
    output = cStringIO.StringIO()
    output.write(val_str)
    output.seek(0)
    cur.copy_from(output, tbl, sep='\t', columns=(cols))
    conn.commit()

それが問題だとは思わないので、私はxml解析を含めていません。dbがないと、パーサーは2分以内に実行されます。

4

3 に答える 3

2

テーブルが大きくなるにつれて挿入が遅くなる可能性があるものがいくつかあります。

  • DBが大きくなるにつれてより多くの作業を行う必要があるトリガー
  • インデックスは、成長するにつれて更新に費用がかかります

重要でないトリガーを無効にするか、それが不可能な場合は、一定時間で実行されるように再設計します。

インデックスを削除し、データが読み込まれた後に作成します。INSERT実際のsまたはsのインデックスが必要な場合は、それらのインデックスUPDATEを維持する必要があります。

たくさんのことをしている場合は、テーブルを定期的に実行するか、autovacuumを非常に積極的に実行するように設定することUPDATEを検討してください。VACUUMこれは、Pgがファイルシステムから新しいスペースをより高価に割り当てるのではなく、スペースを再利用するのに役立ち、テーブルの肥大化を回避するのに役立ちます。

また、作業ブロックごとに再接続しないことで時間を節約できます。接続を維持します。

于 2012-08-25T03:43:03.470 に答える
0

個人的な経験から、copy_fromは、何かをコミットした後はインデックスを更新しないため、後で更新する必要があります。すべての挿入が終了したら、関数の外に移動conn = psycopg2.connect("dbname=<mydb> user=postgres password=<mypw>"); cur = conn.cursor()してcommit()を実行します(約100k行ごとにコミットすることをお勧めします。そうしないと、速度が低下し始めます)。

また、ばかげているように見えるかもしれませんが、私には何度も起こりました。db_updateを呼び出した後、必ずval_strをリセットしてください。私にとって、copy_from / insertsが遅くなり始めたのは、同じ行に加えてより多くの行を挿入しているためです。

于 2012-08-30T16:52:22.693 に答える
0

私は以下を使用していますが、これまで見てきた限り、パフォーマンスに影響はありません。

import psycopg2
import psycopg2.extras

local_conn_string = """
    host='localhost'
    port='5432'
    dbname='backupdata'
    user='postgres'
    password='123'"""
local_conn = psycopg2.connect(local_conn_string)
local_cursor = local_conn.cursor(
    'cursor_unique_name',
     cursor_factory=psycopg2.extras.DictCursor)

実行時をテストするために、コードで次の出力を作成しました(そして、多くの行を解析しています。30.000.000以上)。

Parsed 2600000 rows in 00:25:21
Parsed 2700000 rows in 00:26:19
Parsed 2800000 rows in 00:27:16
Parsed 2900000 rows in 00:28:15
Parsed 3000000 rows in 00:29:13
Parsed 3100000 rows in 00:30:11

私は何も「コピー」しないことに言及しなければなりません。しかし、私は行をリモートのPostGreSQLからローカルのPostGreSQLに移動しています。その過程で、30.000.000 +は通常のクエリでは処理するには少なすぎるため、データのインデックスを作成するためにさらにいくつかのテーブルを作成します。

注意:時間は上向きにカウントされており、クエリごとではありません。

それは私のcursor創造の仕方に関係していると思います。

編集1:

私はクエリを実行するために以下を使用しています:

local_cursor.execute("""SELECT * FROM data;""")

row_count = 0
for row in local_cursor:
    if(row_count % 100000 == 0 and row_count != 0):
        print("Parsed %s rows in %s" % (row_count,
                                        my_timer.get_time_hhmmss()
                                        ))
    parse_row(row)
    row_count += 1

print("Finished running script!")
print("Parsed %s rows" % row_count)

これmy_timerは私が作成したタイマークラスであり、parse_row(row)関数はデータをフォーマットし、ローカルDBに転送し、データがローカルDBに移動されたことが確認されると、最終的にリモートDBから削除します。

EDIT2:

約4.000.000のクエリを解析した後でも、DB内の100.000行ごとに解析するのに約1分かかります。

Parsed 3800000 rows in 00:36:56
Parsed 3900000 rows in 00:37:54
Parsed 4000000 rows in 00:38:52
Parsed 4100000 rows in 00:39:50
于 2016-11-22T13:49:53.000 に答える