【问题标题】:PostgreSQL - how to run VACUUM from code outside transaction block?PostgreSQL - 如何从事务块外的代码运行 VACUUM?
【发布时间】:2010-11-04 06:51:09
【问题描述】:

我正在将 Python 与 psycopg2 一起使用,并且我正在尝试在插入数千行的日常操作之后运行完整的 VACUUM。问题是当我尝试在我的代码中运行VACUUM 命令时,出现以下错误:

psycopg2.InternalError: VACUUM cannot run inside a transaction block

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

如果有什么不同,我有一个简单的 DB 抽象类,它的子集显示在下面的上下文中(不可运行,省略异常处理和文档字符串,并进行了跨行调整):

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)

【问题讨论】:

  • @nosklo,好建议,但根据 Postgres 文档,这与 COMMIT 相同。
  • 您是否在使用 SQLAlchemy?我遇到了类似的问题,因为在 SqlAlchemy 中设置 autocommit=True 并没有实际上关闭事务。使用 set_isolation_level 是一种访问 psycopg2 连接的内部方法的变通方法。
  • @MichaelAquilina 我相信当时(现在是 6 年前)这是一个不使用 ORM 的项目的一部分。

标签: python sql postgresql psycopg2 vacuum


【解决方案1】:

对于其他尝试过所有有关此问题的建议但均未成功的其他人,您可能会遭受与我相同的命运:我在一个 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")

【讨论】:

    【解决方案2】:

    经过更多搜索,我发现了 psycopg2 连接对象的isolation_level 属性。事实证明,将其更改为0 会将您移出事务块。将上述类的vacuum方法改成如下即可解决。请注意,我还将隔离级别设置回原来的状态以防万一(默认情况下似乎是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(接近该页面的末尾)提供了在此上下文中隔离级别的简要说明。

    【讨论】:

    • 或者,避免幻数:self.conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
    【解决方案3】:

    请注意,如果您使用 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】:

      虽然在当前版本的 postgresql 中,vacuum full 存在问题,但在某些大规模操作后强制执行“vacuum analyze”或“reindex”可以提高性能或清理磁盘使用情况。这是 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 的访问。

      【讨论】:

        【解决方案5】:

        此外,您还可以使用以下方式获取 Vacuum 或 Analyze 给出的消息:

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

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

        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 很有用^^

        【讨论】:

        • 您需要运行 cursor.execute('VACUUM FULL VERBOSE') 才能真正获得该属性中的某些内容。
        【解决方案6】:

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

        【讨论】:

        • 根据您的使用模式,有时手动吸尘是有意义的,恕我直言。
        • 有,但现在没有那么多了。它绝对不应该是 VACUUM FULL。
        • 我正在进入 PostGres 并使用一些大表。所有的书(从 8.* 或 9.* 的角度讲)都谈到在大量更新后手动运行 VACUUM ANALYZE,或者使用守护进程自动运行。
        • “在插入几千行的日常操作之后”,这样的实用程序在完成后绝对应该是 VACUUM。
        • 巨大的不同,但是在大量更新之后,您确实需要VACUUM ANALYZE,无论它是由您触发还是由 autovacuum 触发。 “你甚至不需要手动运行普通的 VACUUM”本身有点误导。
        【解决方案7】:

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

        但在我看来,PostgreSQL 的工作方式可能与 SQLite 类似,它有两种操作模式:

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

        在这种情况下,问题可能出在访问层 psycopg2 内部。当它通常以隐式插入事务直到提交之前的方式运行时,可能没有“标准方式”来制造真空。

        当然,“psycopg2”有其特殊的“vacuum”方法或特殊操作模式,在这种模式下不会启动隐式事务。

        当不存在这样的可能性时,只剩下一个选项(不改变访问层;-)):

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

        【讨论】:

        • 感谢您的详细解答。事实证明,解决方案与“隔离级别”有关,请参阅下面的答案。
        猜你喜欢
        • 2021-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-14
        • 1970-01-01
        • 1970-01-01
        • 2013-10-09
        相关资源
        最近更新 更多