2009-06-19 19 views
25

Tôi đang sử dụng Python với psycopg2 và tôi đang cố gắng chạy toàn bộ VACUUM sau một thao tác hàng ngày chèn hàng nghìn hàng. Vấn đề là khi tôi cố gắng chạy lệnh VACUUM trong mã của tôi, tôi nhận được lỗi sau:PostgreSQL - cách chạy VACUUM từ mã bên ngoài khối giao dịch?

psycopg2.InternalError: VACUUM cannot run inside a transaction block 

Làm thế nào để chạy này từ mã bên ngoài một khối giao dịch?

Nếu nó làm cho một sự khác biệt, tôi có một lớp trừu tượng DB đơn giản, một tập hợp con trong số đó sẽ được hiển thị dưới đây để biết ngữ cảnh (không Runnable, xử lý ngoại lệ và docstrings bỏ qua và đường kéo dài điều chỉnh thực hiện):

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

thử gửi GIAO DỊCH END? – nosklo

+0

@nosklo, Đề xuất tốt, nhưng theo tài liệu Postgres giống với COMMIT. –

+0

Bạn có đang sử dụng SQLAlchemy không? Tôi đã gặp một vấn đề tương tự vì thiết lập autocommit = True trong SqlAlchemy không * thực sự * tắt giao dịch. Sử dụng 'set_isolation_level' là một công việc xung quanh truy cập các phương thức nội bộ của kết nối psycopg2. –

Trả lời

49

Sau khi tìm kiếm thêm, tôi đã phát hiện thuộc tính isolation_level của đối tượng kết nối psycopg2. Hóa ra việc thay đổi điều này thành 0 sẽ chuyển bạn ra khỏi một khối giao dịch. Thay đổi phương pháp chân không của lớp trên để giải quyết nó sau. Lưu ý rằng tôi cũng thiết lập mức cách ly về mức mà trước đây nó chỉ trong trường hợp (có vẻ là 1 theo mặc định).

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 (gần cuối trang) cung cấp giải thích ngắn gọn về mức cách ly trong ngữ cảnh này.

+8

Hoặc, tránh các số ma thuật: 'self.conn.set_isolation_level (psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)' –

1

Tôi không biết psycopg2 và PostgreSQL, nhưng chỉ apsw và SQLite, vì vậy tôi nghĩ rằng tôi không thể cung cấp cho một "psycopg2" giúp đỡ.

Nhưng nó seams với tôi, rằng PostgreSQL có thể làm việc tương tự như SQLite không, nó có hai chế độ hoạt động:

  • Bên ngoài một khối giao dịch: Đây là ngữ nghĩa tương đương để có một khối giao dịch xung quanh mỗi SQL đơn hoạt động
  • bên trong một khối giao dịch, được đánh dấu bởi "BEGIN TRANSACTION" và kết thúc bằng "END gIAO DỊCH"

Khi điều này là trường hợp, vấn đề có thể là bên trong psycopg2 lớp truy cập. Khi nó hoạt động bình thường theo cách thức giao dịch được chèn ngầm cho đến khi một cam kết được thực hiện, có thể không có "cách tiêu chuẩn" để tạo chân không. Tất nhiên nó có thể là có thể, rằng "psycopg2" có phương pháp "chân không" đặc biệt của nó, hoặc một chế độ hoạt động đặc biệt, nơi không có giao dịch ngầm được bắt đầu.

Khi không có khả năng như vậy tồn tại, có ở lại một lựa chọn duy nhất (mà không thay đổi lớp truy cập ;-)):

Hầu hết các cơ sở dữ liệu có một programm vỏ để truy cập cơ sở dữ liệu. Chương trình có thể chạy chương trình shell này với một đường ống (nhập lệnh chân không vào trong shell), do đó sử dụng chương trình shell để tạo chân không. Vì chân không là một hoạt động chậm như vậy, sự khởi đầu của một chương trình bên ngoài sẽ bị bỏ qua. Tất nhiên, chương trình thực tế phải cam kết tất cả các công việc không được đề cập trước đây, nếu không có thể có tình trạng khóa chết - chân không phải đợi đến khi kết thúc giao dịch cuối cùng của bạn.

+1

Cảm ơn câu trả lời chi tiết của bạn. Nó chỉ ra giải pháp là để làm với "mức cô lập", xem câu trả lời của tôi dưới đây. –

-3

Đừng làm điều đó - bạn không cần VACUUM FULL. Trên thực tế, nếu bạn chạy phiên bản gần đây của Postgres (giả sử> 8.1), bạn thậm chí không cần chạy VACUUM trơn tru theo cách thủ công.

+6

Tùy thuộc vào các mẫu sử dụng của bạn, vẫn có những lúc cần thiết để hút imho thủ công. – rfusca

+1

Có, nhưng không còn nhiều nữa. Và nó chắc chắn không phải là VACUUM FULL. –

+0

Tôi đang tham gia PostGres và với một số bảng lớn. Tất cả các sách (nói từ một quan điểm 8. * hoặc 9. *) nói về việc chạy VACUUM ANALYZE theo cách thủ công sau rất nhiều cập nhật, hoặc tự động với một daemon. – winwaed

3

Ngoài ra, bạn cũng có thể nhận được các thông điệp được đưa ra bởi chân không hoặc Phân tích sử dụng:

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

lệnh in này một danh sách với thông điệp log của các truy vấn như chân không và Phân tích:

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" 

Điều này có thể hữu ích đối với các DBAs ^^

4

Trong khi chân không đầy đủ có vấn đề trong các phiên bản hiện tại của postgresql, buộc một 'phân tích chân không' hoặc 'reindex' sau một số lượng lớn hành động có thể cải thiện hiệu suất hoặc làm sạch mức sử dụng đĩa. Đây là postgresql cụ thể, và cần phải được dọn dẹp để làm điều đúng cho các cơ sở dữ liệu khác.

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) 

Rất tiếc, proxy kết nối do django cung cấp không cấp quyền truy cập set_isolation_level.

2

Lưu ý nếu bạn đang sử dụng Django with South để thực hiện di chuyển, bạn có thể sử dụng mã sau để thực hiện 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() 
Các vấn đề liên quan