2009-06-19 21 views
25

Sto usando Python con psycopg2 e sto cercando di eseguire un intero VACUUM dopo un'operazione giornaliera che inserisce diverse migliaia di righe. Il problema è che quando si tenta di eseguire il comando VACUUM nel mio codice ottengo il seguente errore:PostgreSQL: come eseguire VACUUM dal codice al di fuori del blocco di transazione?

psycopg2.InternalError: VACUUM cannot run inside a transaction block 

Come si esegue questo dal codice all'esterno di un blocco di transazione?

Se si fa la differenza, ho una semplice classe di astrazione DB, un sottoinsieme di che è visualizzato sotto per il contesto (non eseguibile, gestione delle eccezioni e docstring omessi e le rettifiche della linea che attraversa fatto):

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

provare a inviare END TRANSACTION? – nosklo

+0

@nosklo, buon suggerimento, ma secondo i documenti Postgres è lo stesso di COMMIT. –

+0

Stai usando SQLAlchemy per caso? Ho riscontrato un problema simile perché l'impostazione autocommit = True in SqlAlchemy non * effettivamente * disattiva le transazioni. L'uso di 'set_isolation_level' è un modo per aggirare che accede ai metodi interni della connessione psycopg2. –

risposta

49

Dopo ulteriori ricerche, ho scoperto la proprietà isolation_level dell'oggetto connessione psycopg2. Si scopre che la modifica di questo a 0 ti sposterà fuori da un blocco di transazione. Cambiando il metodo del vuoto della classe di cui sopra al seguente lo risolve. Si noti che ho anche reimpostato il livello di isolamento su ciò che era precedentemente nel caso (sembra essere 1 per impostazione predefinita).

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 (vicino alla fine in quella pagina) fornisce una breve spiegazione di livelli di isolamento in questo contesto.

+8

Oppure, evitando i numeri magici: 'self.conn.set_isolation_level (psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)' –

1

Non conosco psycopg2 e PostgreSQL, ma solo apsw e SQLite, quindi penso di non poter dare un aiuto "psycopg2".

Ma cuciture a me, che PostgreSQL potrebbe funzionare simile come fa SQLite, ha due modalità di funzionamento:

  • all'esterno di un blocco di transazione: Questo è semanticamente equivalente ad avere un blocco di transazione intorno ad ogni singola SQL funzionamento
  • all'interno di un blocco di transazione, che viene indicato con "BEGIN TRANSACTION" e conclusa con "END tRANSAZIONE"

Quando questo è il caso, il problema potrebbe essere all'interno della psycopg2 livello di accesso. Quando normalmente funziona in modo che le transazioni siano inserite implicitamente fino a quando viene eseguito un commit, non ci può essere un "modo standard" per creare un vuoto.

Ovviamente potrebbe essere possibile che "psycopg2" disponga del suo speciale metodo "vuoto" o di una modalità operativa speciale, in cui non vengono avviate transazioni implicite.

Quando tali possibilità esiste, ci rimane una sola opzione (senza modificare il livello di accesso ;-)):

maggior parte dei database hanno un programma di shell per accedere al database. Il programma potrebbe eseguire questo programma shell con una pipe (inserendo il comando vacuum nella shell), usando quindi il programma shell per creare il vuoto. Poiché il vuoto è un'operazione lenta come tale, l'inizio di un programma esterno sarà trascurabile. Ovviamente, il programma attuale dovrebbe impegnare tutto il lavoro non eseguito prima, altrimenti potrebbe esserci una situazione di blocco morto: il vuoto deve attendere fino alla fine dell'ultima transazione.

+1

Grazie per la tua risposta dettagliata. Risulta che la soluzione riguardava i "livelli di isolamento", vedi la mia risposta qui sotto. –

-3

Non farlo - non è necessario VUOTO PIENO. In realtà se esegui una versione un po 'recente di Postgres (diciamo> 8.1) non hai nemmeno bisogno di eseguire manualmente VACUUM.

+6

A seconda dei modelli di utilizzo, ci sono ancora momenti in cui è opportuno eseguire l'aspirazione manuale dell'imho. – rfusca

+1

Ce ne sono, ma non ce ne sono più tanti. E sicuramente non dovrebbe essere VACUUM PIENO. –

+0

Sto entrando in PostGres e con alcuni grandi tavoli. Tutti i libri (parlando da una prospettiva 8. * o 9. *) parlano di eseguire VACUUM ANALYZE manualmente dopo un sacco di aggiornamenti, o automaticamente con un demone. – winwaed

3

Inoltre, è anche possibile ottenere i messaggi dati dalla depressione o analizzare utilizzando:

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

questa stampa comando una lista con il messaggio di log delle query come vuoto e analizzare:

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" 

Questo può essere utile per i DBA ^^

4

Mentre vuoto pieno è discutibile nelle versioni correnti di postgresql, costringendo un 'analisi del vuoto' o 'reindex' dopo un certo massiccio le azioni possono migliorare le prestazioni o ripulire l'utilizzo del disco. Questo è postgresql specifico e deve essere ripulito per fare la cosa giusta per altri database.

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) 

Purtroppo il proxy connessione fornita da django non fornisce l'accesso a set_isolation_level.

2

Nota se si sta utilizzando Django con South per eseguire una migrazione, è possibile utilizzare il seguente codice per eseguire 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()