PostgreSQL - 如何从事务块外的代码运行VACUUM?

时间:2009-06-19 11:26:33

标签: python sql postgresql psycopg2 vacuum

我正在使用带有psycopg2的Python,并且我试图在每天插入数千行的操作之后运行完整的VACUUM。问题是,当我尝试在我的代码中运行VACUUM命令时,我收到以下错误:

psycopg2.InternalError: VACUUM cannot run inside a transaction block

如何从事务块外的代码运行它?

如果它有所不同,我有一个简单的DB抽象类,下面显示了上下文的子集(不是runnable,省略了异常处理和docstrings以及进行了行跨越调整):

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)

7 个答案:

答案 0 :(得分:52)

经过更多搜索后,我发现了psycopg2连接对象的isolation_level属性。事实证明,将此更改为0会使您退出事务处理块。将上述类的真空方法改为以下解决方案。请注意,我还将隔离级别设置回以前的情况(默认情况下似乎为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(在该页面的末尾附近)提供了此上下文中隔离级别的简要说明。

答案 1 :(得分:4)

此外,您还可以使用以下方法获取Vacuum或Analyze提供的消息:

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

此命令打印一个列表,其中包含查询的日志消息,如Vacuum和Analyze:

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"

这对DBA ^^

很有用

答案 2 :(得分:4)

虽然在当前版本的postgresql中真空充满是有问题的,但在某些大规模操作之后强制进行“真空分析”或“重新索引”可以提高性能或清理磁盘使用情况。这是postgresql特有的,需要清理以便为其他数据库做正确的事情。

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)

不幸的是,django提供的连接代理不提供对set_isolation_level的访问。

答案 3 :(得分:2)

请注意,如果您使用Django with South执行迁移,则可以使用以下代码执行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()

答案 4 :(得分:2)

对于尝试了解决此问题的所有建议但没有成功的其他任何人,您可能会遭受与我相同的命运:我在一次 execute() 调用中有 2 个(或更多)SQL 语句。事实证明,Postgres 本身会在第一条语句(由 ; 分隔)之后重置任何自动提交/隔离。我终于在这里找到了解决方案:https://github.com/psycopg/psycopg2/issues/1201

所以不要做这样的事情:

cursor.execute("SELECT 1; VACUUM FULL")

改为:

cursor.execute("SELECT 1")
cursor.execute("VACUUM FULL")

答案 5 :(得分:1)

我不知道psycopg2和PostgreSQL,但只有apsw和SQLite,所以我觉得我不能给“psycopg2”帮助。

但它对我来说,PostgreSQL可能与SQLite类似,它有两种操作模式:

  • 在事务块之外:这在语义上等同于在每个SQL操作周围都有一个事务块
  • 在交易区内,由“BEGIN TRANSACTION”标记并以“END TRANSACTION”结束

在这种情况下,问题可能出在访问层psycopg2内部。当它正常运行时,交易被隐含地插入,直到提交为止,可能没有“标准方法”来实现真空。

当然有可能,“psycopg2”有其特殊的“真空”方法,或特殊的操作模式,没有启动隐式事务。

如果不存在这样的可能性,则只保留一个选项(不更改访问层;-)):

大多数数据库都有一个shell程序来访问数据库。该程序可以使用管道运行此shell程序(将vacuum命令输入shell),从而使用shell程序来实现真空。由于真空是一个缓慢的操作,外部程序的启动将是可以忽略的。当然,实际程序应该在之前完成所有未完成的工作,否则可能会出现死锁情况 - 真空必须等到最后一次交易结束。

答案 6 :(得分:-3)

不要这样做 - 你不需要VACUUM FULL。实际上如果你运行稍微版本的Postgres(比如说> 8.1),你甚至不需要手动运行普通的VACUUM。