2009-06-19 28 views
25

Używam Pythona z psycopg2 i próbuję uruchomić pełny VACUUM po codziennej operacji, która wstawia kilka tysięcy wierszy. Problem polega na tym, że gdy próbuję uruchomić komendę VACUUM wewnątrz mojego kodu pojawia się następujący błąd:PostgreSQL - jak uruchomić VACUUM z kodu poza blokiem transakcji?

psycopg2.InternalError: VACUUM cannot run inside a transaction block 

Jak uruchomić ten z kodem poza blokiem transakcji?

Jeśli to robi różnicę, mam prosty DB klasę abstrakcji, podzbiór, który jest wyświetlany poniżej kontekście (nie runnable, obsługi wyjątków i docstrings pominiętych i korekty linii obejmujących złożony):

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

spróbować wysłać KONIEC TRANSAKCJI? – nosklo

+0

@nosklo, Dobra sugestia, ale według dokumentów Postgresa jest taka sama jak COMMIT. –

+0

Czy używasz SQLAlchemy przez przypadek? Doświadczyłem podobnego problemu, ponieważ ustawienie autocommit = True w SqlAlchemy nie * faktycznie * wyłącza transakcji. Używanie 'set_isolation_level' jest pracą wokół, która uzyskuje dostęp do wewnętrznych metod połączenia psycopg2. –

Odpowiedz

49

Po kolejnych poszukiwaniach odkryłem właściwość izolacji obiektu powiązania psycopg2. Okazuje się, że zmiana tego na 0 spowoduje wyprowadzenie Cię z bloku transakcji. Zmiana metody próżni powyższej klasy na następującą rozwiązuje ją. Zauważ, że ustawiłem poziom izolacji z powrotem na to, co poprzednio było na wszelki wypadek (domyślnie jest to 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) 

This article (pod koniec na tej stronie) zawiera krótkie wyjaśnienie poziomów izolacji w tym kontekście.

+8

Lub, unikając magicznych liczb: 'self.conn.set_isolation_level (psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)' –

1

Nie znam psycopg2 i PostgreSQL, ale tylko apsw i SQLite, więc myślę, że nie mogę dać pomocy "psycopg2".

ale szwy na mnie, że PostgreSQL może działać podobnie jak czyni SQLite, posiada dwa tryby pracy:

  • zewnątrz bloku transakcji: To jest semantycznie równoważne mieć blok transakcji wokół każdego SQL operacja
  • wewnątrz bloku transakcji, która jest oznaczona przez „rozpocząć transakcji” i zakończone „końca transakcji”

Gdy jest to przypadek, problem może być wewnątrz psycopg2 warstwy dostępu. Kiedy normalnie działa w taki sposób, że transakcje są wprowadzane implicite aż do zatwierdzenia, nie może istnieć "standardowy sposób", aby uzyskać próżnię.

Oczywiście możliwe jest, że "psycopg2" ma swoją specjalną metodę "próżniową" lub specjalny tryb pracy, w którym nie są wykonywane żadne transakcje niejawne.

W razie braku takich możliwości, nie pozostaje pojedynczą opcję (bez zmiany warstwy dostępu ;-)):

Większość baz danych mają programm powłoki w celu uzyskania dostępu do bazy danych. Program może uruchomić ten program powłoki za pomocą potoku (wprowadzając komendę próżni do powłoki), wykorzystując w ten sposób program powłoki do wytworzenia próżni. Ponieważ próżnia jest powolną operacją jako taką, uruchomienie zewnętrznego programu będzie zaniedbywalne. Oczywiście, rzeczywisty program powinien zobowiązać się do wykonania wszystkich nieopłaconych zadań wcześniej, w przeciwnym razie może istnieć sytuacja, w której nie ma blokady - próżnia musi czekać do końca ostatniej transakcji.

+1

Dziękujemy za szczegółową odpowiedź. Okazuje się, że rozwiązanie polega na "poziomach izolacji", zobacz moją odpowiedź poniżej. –

-3

Nie rób tego - nie potrzebujesz VACUUM FULL. Właściwie, jeśli uruchomisz nieco najnowszą wersję PostgreSy (powiedzmy> 8.1), nie musisz nawet uruchamiać zwykłego VACUUM ręcznie.

+6

Zależnie od twoich wzorców użycia, są jeszcze chwile, w których warto odkurzać ręcznie imho. – rfusca

+1

Istnieje, ale nie ma ich już więcej. I zdecydowanie nie powinno to być PRÓŻNE PEŁNE. –

+0

Dostaję się do PostGres i niektórych dużych tabel. Wszystkie książki (mówiące z perspektywy 8. * lub 9. *) mówią o uruchomieniu ANALIZA PRÓŻNI ręcznie po wielu aktualizacjach lub automatycznie za pomocą demona. – winwaed

3

Dodatkowo można również uzyskać wiadomości podanych przez próżni lub analizować przy użyciu:

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

ten nadruk komenda listę z komunikatu dziennika zapytań jak próżniowe oraz analizuje:

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" 

Może to być przydatne dla DBAs ^^

4

Podczas gdy próżnia pełna jest wątpliwa w aktualnych wersjach postgresql, wymuszając "analizę próżni" lub "reindeks" po pewnej masywności działania mogą poprawić wydajność lub wyczyścić użycie dysku. Jest to specyficzne dla PostgreSQL i należy je wyczyścić, aby zrobić właściwe rzeczy dla innych baz danych.

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) 

Niestety proxy połączenia dostarczone przez django nie zapewnia dostępu do poziomu set_isolation_level.

2

Uwaga: jeśli używasz Django z południem do przeprowadzenia migracji, możesz użyć poniższego kodu, aby wykonać 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()