python - example - PostgreSQL: ¿cómo ejecutar VACUUM desde el código fuera del bloque de transacción?
python postgre (6)
Estoy usando Python con psycopg2 y estoy tratando de ejecutar un VACUUM
completo después de una operación diaria que inserta varios miles de filas. El problema es que cuando intento ejecutar el comando VACUUM
dentro de mi código, aparece el siguiente error:
psycopg2.InternalError: VACUUM cannot run inside a transaction block
¿Cómo ejecuto esto desde el código fuera de un bloque de transacción?
Si hace una diferencia, tengo una simple clase de abstracción de base de datos, un subconjunto de la cual se muestra a continuación para el contexto (no ejecutable, manejo de excepciones y docstrings omitidos y ajustes de extensión de línea realizados):
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)
Si bien el vacío total es cuestionable en las versiones actuales de postgresql, forzar un ''análisis de vacío'' o ''reindexar'' después de ciertas acciones masivas puede mejorar el rendimiento o limpiar el uso del disco. Esto es específico de postgresql, y debe limpiarse para hacer lo correcto para otras bases de datos.
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)
Lamentablemente, el proxy de conexión proporcionado por django no proporciona acceso a set_isolation_level.
Después de más búsquedas, descubrí la propiedad isolation_level del objeto de conexión psycopg2. Resulta que cambiar esto a 0
sacará de un bloque de transacción. Cambiar el método de vacío de la clase anterior a lo siguiente lo resuelve. Tenga en cuenta que también configuré el nivel de aislamiento como anteriormente solo por si acaso (parece ser 1
por defecto).
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)
Este artículo (cerca del final en esa página) proporciona una breve explicación de los niveles de aislamiento en este contexto.
No lo hagas, no necesitas VACÍO COMPLETO. En realidad, si ejecuta una versión algo más reciente de Postgres (digamos> 8.1), no necesita ejecutar manualmente VACUUM.
No sé psycopg2 y PostgreSQL, pero solo apsw y SQLite, así que creo que no puedo dar una ayuda "psycopg2".
Pero me parece que PostgreSQL podría funcionar de manera similar a SQLite, tiene dos modos de operación:
- Fuera de un bloque de transacción: Esto es semánticamente equivalente a tener un bloque de transacción alrededor de cada operación de SQL
- Dentro de un bloque de transacción, que está marcado por "BEGIN TRANSACTION" y terminado por "END TRANSACTION"
Cuando este es el caso, el problema podría estar dentro de la capa de acceso psycopg2. Cuando normalmente funciona de manera tal que las transacciones se insertan implícitamente hasta que se realiza una confirmación, no puede haber una "forma estándar" de crear un vacío.
Por supuesto, podría ser posible que "psycopg2" tenga su método especial de "vacío", o un modo de operación especial, donde no se inician transacciones implícitas.
Cuando no existen tales posibilidades, queda una sola opción (sin cambiar la capa de acceso ;-)):
La mayoría de las bases de datos tienen un programa shell para acceder a la base de datos. El programa podría ejecutar este programa de shell con una tubería (ingresando el comando de vacío en la carcasa), utilizando así el programa shell para hacer la aspiradora. Como el vacío es una operación lenta como tal, el inicio de un programa externo será negligente. Por supuesto, el programa real debería comprometer todo el trabajo sin compromiso antes, de lo contrario podría haber una situación de bloqueo muerto: el vacío debe esperar hasta el final de su última transacción.
Además, también puede obtener los mensajes dados por el vacío o analizar utilizando:
>> print conn.notices #conn is the connection object
este comando imprime una lista con el mensaje de registro de consultas como Aspirar y Analizar:
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"
Esto puede ser útil para los DBAs ^^
Tenga en cuenta que si está usando Django con South para realizar una migración, puede usar el siguiente código para ejecutar un 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()