asyncpg连接池的execute和连接的execute有什么区别?

4

从连接池中获取连接,然后在连接上调用execute的使用场景有哪些?相比直接在连接池对象上调用execute,这样做有什么好处?

Pool 类的文档中给出了如下示例:

con = await pool.acquire()
try:
    await con.execute(...)
finally:
    await pool.release(con)

从池中获取连接并用于执行语句。与直接在Pool对象上调用execute方法相比,这样做的好处是什么?
async with asyncpg.create_pool(user=pg_user,
                               password=pg_pass,
                               host=pg_host,
                               port=pg_port,
                               database=pg_db,
                               command_timeout=60) as pool:
    pool.execute(f'TRUNCATE TABLE {table};')
Pool.execute 的文档甚至声明了以下内容:

执行SQL命令(或一组命令)。

池使用其中一个连接来执行此操作。除此之外,它的行为与 Connection.execute() 相同。

1个回答

4

从连接池获取连接,然后调用连接的execute方法,与直接调用池对象的execute方法相比,有哪些使用场景?

当您显式地获取连接时,可以启动事务并在事务中执行多个查询:

async def transfer_money(pool, from_id, to_id, amount):
    async with pool.acquire() as conn:
        async with conn.transaction():
            await conn.execute(
                "UPDATE balance SET money = money + $2 WHERE user_id = $1",
                to_id, amount
            )
            await conn.execute(
                "UPDATE balance SET money = money - $2 WHERE user_id = $1",
                from_id, amount
            )

如果你这样做了:
async def transfer_money(pool, from_id, to_id, amount):
    await pool.execute(
        "UPDATE balance SET money = money + $2 WHERE user_id = $1",
        to_id, amount
    )
    await pool.execute(
        "UPDATE balance SET money = money - $2 WHERE user_id = $1",
        from_id, amount
    )

这将是一个简写:

async def transfer_money(pool, from_id, to_id, amount):
    async with pool.acquire() as conn:
        await conn.execute(
            "UPDATE balance SET money = money + $2 WHERE user_id = $1",
            to_id, amount
        )
    async with pool.acquire() as conn:
        await conn.execute(
            "UPDATE balance SET money = money - $2 WHERE user_id = $1",
            from_id, amount
        )

如果程序在第一个查询后崩溃,例如,一个用户会得到钱,但另一个用户不会失去它。换句话说,该函数不是原子的(ACID中的“A”)。 Pool.execute的实现方式如下(GitHub link):
    async def execute(self, query: str, *args, timeout: float=None) -> str:
        async with self.acquire() as con:
            return await con.execute(query, *args, timeout=timeout)

感谢清晰的解释。如果我理解正确,Pool.execute会立即打开和关闭一个事务。而在连接对象上执行(Connection.execute)则允许更自由地决定在单个连接上提交(或回滚)什么。如果没有在上下文管理器中调用Connection.execute呢?那么这两种方法(Connection.executePool.execute)本质上是相同的吗? - Ryan
1
此外,在 asyncpg transactions docs 中还指出:"当不在一个明确的事务块中时,对数据库的任何更改都将立即应用。这也被称为自动提交。"因此,在您的第二个示例中,即使它们都在同一个连接上下文管理器中,每次调用 conn.execute 都会在自己的事务中立即执行吗? - Ryan
@Ryan,你说得对,是我完全给你提供了错误信息。单独获取连接并不会启动一个事务,你需要显式地调用 conn.transaction()。我已经更新了回答。 - decorator-factory

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接