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)
さらに検索した後、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)
この記事 (そのページの終わり近く)は、このコンテキストでの分離レベルの簡単な説明を提供します。
さらに、以下を使用して、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に役立ちます^^
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へのアクセスを提供しません。
移行を実行するために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()
私はpsycopg2とPostgreSQLを知りませんが、apswとSQLiteしか知らないので、「psycopg2」の助けを与えることはできないと思います。
しかし、PostgreSQLはSQLiteと同じように機能する可能性があり、2つの操作モードがあります。
この場合、問題はアクセス層psycopg2の内部にある可能性があります。コミットが行われるまでトランザクションが暗黙的に挿入されるように通常動作する場合、バキュームを作成する「標準的な方法」はありません。
もちろん、「psycopg2」に特別な「バキューム」メソッド、または暗黙のトランザクションが開始されない特別な操作モードがある可能性もあります。
そのような可能性が存在しない場合、1つのオプションが残ります(アクセスレイヤーを変更せずに;-)):
ほとんどのデータベースには、データベースにアクセスするためのシェルプログラムがあります。プログラムは、パイプを使用してこのシェルプログラムを実行し(シェルにvacuum-commandを入力)、シェルプログラムを使用してバキュームを作成できます。バキュームはそれ自体が遅い操作であるため、外部プログラムの開始は無視できます。もちろん、実際のプログラムは、コミットされていないすべての作業を前にコミットする必要があります。そうしないと、デッドロック状態になる可能性があります。バキュームは、最後のトランザクションが終了するまで待機する必要があります。