web-dev-qa-db-ja.com

PostgreSQL-トランザクションブロック外のコードからVACUUMを実行する方法は?

Psycopg2でPythonを使用していて、数千行を挿入する毎日の操作の後に完全なVACUUMを実行しようとしています。問題は、実行しようとするとコード内のVACUUMコマンドで次のエラーが発生します。

psycopg2.InternalError: VACUUM cannot run inside a transaction block

トランザクションブロック外のコードからこれを実行するにはどうすればよいですか?

違いが生じる場合は、単純なDB抽象化クラスがあり、そのサブセットがコンテキスト用に以下に表示されます(実行不可、例外処理とdocstringが省略され、行間調整が行われます)。

class db(object):
    def __init__(dbname, Host, port, user, password):
        self.conn = psycopg2.connect("dbname=%s Host=%s port=%s \
                                      user=%s password=%s" \
                                      % (dbname, Host, port, user, password))

        self.cursor = self.conn.cursor()

    def _doQuery(self, query):
        self.cursor.execute(query)
        self.conn.commit()

    def vacuum(self):
        query = "VACUUM FULL"
        self._doQuery(query)
34
Wayne Koorts

さらに検索した後、psycopg2接続オブジェクトのisolation_levelプロパティを発見しました。これを0に変更すると、トランザクションブロックから移動することがわかります。上記のクラスのバキュームメソッドを次のように変更すると、問題が解決します。また、念のために分離レベルを以前のレベルに戻したことにも注意してください(デフォルトでは1のようです)。

def vacuum(self):
    old_isolation_level = self.conn.isolation_level
    self.conn.set_isolation_level(0)
    query = "VACUUM FULL"
    self._doQuery(query)
    self.conn.set_isolation_level(old_isolation_level)

この記事 (そのページの終わり近く)は、このコンテキストでの分離レベルの簡単な説明を提供します。

58
Wayne Koorts

さらに、以下を使用して、VacuumまたはAnalyzeからのメッセージを取得することもできます。

>> print conn.notices #conn is the connection object

このコマンドは、VacuumやAnalyseなどのクエリのログメッセージを含むリストを出力します。

INFO:  "usuario": processados 1 de 1 páginas, contendo 7 registros vigentes e 0 registros não vigentes; 7 registros amostrados, 7 registros totais estimados   
INFO:  analisando "public.usuario"

これはDBAに役立ちます^^

4
Diego Guimaraes

Postgresqlの現在のバージョンではバキュームフルは疑わしいですが、特定の大規模なアクションの後に「バキューム分析」または「再インデックス」を強制すると、パフォーマンスが向上したり、ディスク使用量がクリーンアップされたりします。これはpostgresql固有であり、他のデータベースに対して正しいことを行うためにクリーンアップする必要があります。

from Django.db import connection
# Much of the proxy is not defined until this is done
force_proxy = connection.cursor()
realconn=connection.connection
old_isolation_level = realconn.isolation_level
realconn.set_isolation_level(0)
cursor = realconn.cursor()
cursor.execute('VACUUM ANALYZE')
realconn.set_isolation_level(old_isolation_level)

残念ながら、Djangoによって提供される接続プロキシは、set_isolation_levelへのアクセスを提供しません。

4
Chris Dukes

移行を実行するためにSouthでDjangoを使用している場合は、次のコードを使用してVACUUM ANALYZEを実行できることに注意してください。

def forwards(self, orm):

    db.commit_transaction()
    db.execute("VACUUM ANALYZE <table>")

    #Optionally start another transaction to do some more work...
    db.start_transaction()
2
donturner

私はpsycopg2とPostgreSQLを知りませんが、apswとSQLiteしか知らないので、「psycopg2」の助けを与えることはできないと思います。

しかし、PostgreSQLはSQLiteと同じように機能する可能性があり、2つの操作モードがあります。

  • トランザクションブロックの外側:これは、すべてのSQL操作の周囲にトランザクションブロックがあることと意味的に同等です。
  • トランザクションブロック内で、「BEGIN TRANSACTION」でマークされ、「ENDTRANSACTION」で終了します。

この場合、問題はアクセス層psycopg2の内部にある可能性があります。コミットが行われるまでトランザクションが暗黙的に挿入されるように通常動作する場合、バキュームを作成する「標準的な方法」はありません。

もちろん、「psycopg2」に特別な「バキューム」メソッド、または暗黙のトランザクションが開始されない特別な操作モードがある可能性もあります。

そのような可能性が存在しない場合、1つのオプションが残ります(アクセスレイヤーを変更せずに;-)):

ほとんどのデータベースには、データベースにアクセスするためのシェルプログラムがあります。プログラムは、パイプを使用してこのシェルプログラムを実行し(シェルにvacuum-commandを入力)、シェルプログラムを使用してバキュームを作成できます。バキュームはそれ自体が遅い操作であるため、外部プログラムの開始は無視できます。もちろん、実際のプログラムは、コミットされていないすべての作業を前にコミットする必要があります。そうしないと、デッドロック状態になる可能性があります。バキュームは、最後のトランザクションが終了するまで待機する必要があります。

1
Juergen