2009-06-19 57 views
25

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 trato de ejecutar el comando VACUUM dentro de mi código me sale el siguiente error:PostgreSQL: ¿cómo ejecutar VACUUM desde el código fuera del bloque de transacción?

psycopg2.InternalError: VACUUM cannot run inside a transaction block 

¿Cómo ejecuto esto desde el código fuera de un bloque transacción?

Si se hace una diferencia, tengo una clase simple abstracción DB, un subconjunto de los cuales se muestra a continuación para el contexto (no ejecutable, control de excepciones y docstrings omitidos y los ajustes de línea que abarca hecho):

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) 
+1

intente con el envío END TRANSACTION? – nosklo

+0

@nosklo, buena sugerencia, pero de acuerdo con los documentos de Postgres que es lo mismo que COMPROMETER. –

+0

¿Estás usando SQLAlchemy por casualidad? Experimenté un problema similar porque establecer autocommit = True en SqlAlchemy no * realmente * desactiva las transacciones. Usar 'set_isolation_level' es una solución alternativa que accede a los métodos internos de la conexión psycopg2. –

Respuesta

49

Después de más búsquedas, he descubierto la propiedad isolation_level del objeto de conexión psycopg2. Resulta que cambiar esto a 0 lo 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) 

This article (cerca del final de esa página) proporciona una breve explicación de los niveles de aislamiento en este contexto.

+8

O, evitando los números mágicos: 'self.conn.set_isolation_level (psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)' –

1

No sé psycopg2 y PostgreSQL, pero solo apsw y SQLite, así que creo que no puedo dar una ayuda "psycopg2".

pero las costuras a mí, que PostgreSQL podría funcionar similares como lo hace SQLite, que cuenta con dos modos de funcionamiento:

  • Fuera de un bloque de transacción: Este es semánticamente equivalente a tener un bloque de transacción en torno a cada uno de SQL
  • operación
  • dentro de un bloque de transacción, que está marcada por "BEGIN TRANSACTION" y terminó 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 que podría ser posible, que "psycopg2" tiene su "vacío" especial método, o un modo de funcionamiento especial, donde se inician no hay transacciones implícitas.

Cuando no existe tal posibilidades, hay estancias de una sola opción (sin cambiar la capa de acceso ;-)):

La mayoría de las bases de datos tienen un programm 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.

+1

Gracias por su respuesta detallada. Resulta que la solución tenía que ver con "niveles de aislamiento", ver mi respuesta a continuación. –

-3

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.

+6

Dependiendo de sus patrones de uso, todavía hay momentos en los que tiene sentido hacer una aspiradora manualmente. – rfusca

+1

Hay, pero ya no hay tantos. Y definitivamente no debería ser VACÍO COMPLETO. –

+0

Estoy entrando en PostGres y con algunas tablas grandes. Todos los libros (hablando desde una perspectiva 8. * o 9. *) hablan sobre ejecutar VACUUM ANALYZE manualmente después de muchas actualizaciones, o automáticamente con un daemon. – winwaed

3

Además, también se puede obtener los mensajes dados por el vacío o analizar usando:

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

este comando de impresión de una lista con el mensaje de registro de consultas como de vacío 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 DBA ^^

4

Si bien el vacío completo es cuestionable en las versiones actuales de postgresql, forzando un 'análisis de vacío' o 'reindex' después de cierta cantidad masiva las acciones pueden 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) 

Desafortunadamente, el proxy de conexión proporcionado por django no proporciona acceso a set_isolation_level.

2

Nota: si está utilizando Django con South para realizar una migración, puede usar el siguiente código para ejecutar 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() 
Cuestiones relacionadas