VACUUM
。问题是,当我尝试在代码中运行
VACUUM
命令时,出现以下错误:
psycopg2.InternalError: VACUUM cannot run inside a transaction block
如何从事务块外部的代码运行它?
如果有什么不同,我有一个简单的数据库抽象类,下面显示了其中的一个子集以供上下文使用(不可运行,省略了异常处理和文档字符串,并进行了跨行调整):
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)
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)
>> 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 很有用 ^^
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 的访问。
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()
但我觉得 PostgreSQL 的工作方式可能与 SQLite 类似,它有两种操作模式:
当然有可能,“psycopg2”有其特殊的“真空”方法,或者特殊的操作模式,其中不启动隐式事务。
当不存在这种可能性时,只有一个选项(不更改访问层;-)):
大多数数据库都有一个 shell 程序来访问数据库。该程序可以使用管道运行此 shell 程序(将真空命令输入到 shell 中),从而使用 shell 程序进行真空处理。由于真空本身是一个缓慢的操作,因此外部程序的启动将可以忽略不计。当然,实际的程序应该先提交所有未提交的工作,否则可能会出现死锁情况 - 真空必须等到最后一个事务结束。
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.conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
def _doQuery(self, query):
# THE FOLLOWING LINE OPENS AN IMPLICIT TRANSACTION
# No matter the autocommit settings, this WILL create a transaction
with self.conn:
with conn.cursor() as cursor:
cursor.execute(query)
self.conn.commit()
def vacuum(self):
query = "VACUUM FULL"
self._doQuery(query)
解决方案是不使用连接作为上下文:
def _doQuery(self, query):
# Note no "with conn:" context anymore
with conn.cursor() as cursor:
cursor.execute(query)
self.conn.commit()
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
def vacuum_analyze_table(engine, tablename: str):
# A raw connection is needed, as a standard connection does not expose the `set_isolation_level` method
connection = engine.raw_connection()
# Because a raw connection/cursor is being used, DO NOT WRAP your query with `text(...)` as
# it will throw the error: boolean value of this clause is not defined
vacuum_analyze_query = f'VACUUM ANALYZE {tablename}'
# There is no get_isolation_level method on a raw connection, so to get the original value, grab it directly
old_isolation = connection.isolation_level
# This rectifies the error: psycopg2.errors.ActiveSqlTransaction: VACUUM cannot run inside a transaction block
connection.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
cursor = connection.cursor()
cursor.execute(vacuum_analyze_query)
# Restore isolation level
connection.set_isolation_level(old_isolation)