重用 aiosqlite 连接

reusing aiosqlite connection

我只是不知道如何使用 aiosqlite 模块,以便我可以保留连接供以后使用。

示例基于aiosqlite project page

async with aiosqlite.connect('file.db') as conn:
    cursor = await conn.execute("SELECT 42;")
    rows = await cursor.fetchall()
    print('rows %s' % rows)

工作正常,但我想保持连接,以便我可以在整个程序中使用它。

通常,使用 sqlite,我会打开一个连接,将其存储起来,然后在程序的整个生命周期中使用它。

我也试过类似的东西:

conn = aiosqlite.connect('file.db')
c = await conn.__enter__()
AttributeError: 'Connection' object has no attribute '__enter__'

有没有办法在没有上下文管理器的情况下使用这个模块?

"best" 方法是应用程序的入口点使用上下文管理器方法创建 aiosqlite 连接,在某处存储对连接对象的引用,然后 运行该上下文中应用程序的 "run loop" 方法。这将确保当您的应用程序退出时,正确清理 sqlite 连接。这可能看起来像这样:

async def main():
    async with aiosqlite.connect(...) as conn:
        # save conn somewhere
        await run_loop()

或者,您可以等待适当的 enter/exit 方法:

try:
    conn = aiosqlite.connect(...)
    await conn.__aenter__()
    # do stuff
finally:
    await conn.__aexit__()

无论如何,请注意 aiosqlite 的异步特性确实意味着共享连接可能会导致事务重叠。如果您需要确保并发查询发生在单独的事务中,那么每个事务都需要一个单独的连接。

根据 Python sqlite docs 关于共享连接:

When using multiple threads with the same connection writing operations should be serialized by the user to avoid data corruption.

这同样适用于 aiosqlite 和 asyncio。例如,以下代码可能会将两个插入重叠到一个事务中:

async def one(db):
    await db.execute("insert ...")
    await db.commit()

async def two(db):
    await db.execute("insert ...")
    await db.commit()

async def main():
    async with aiosqlite.connect(...) as db:
        await asyncio.gather(one(db), two(db))

此处正确的解决方案是为每个事务创建一个连接,或者使用类似 executescript 的方法一次执行整个事务。

两点。首先修改语句锁定 whole 数据库,按照:

When a database is accessed by multiple connections, and one of the processes modifies the database, the SQLite database is locked until that transaction is committed. The timeout parameter specifies how long the connection should wait for the lock to go away until raising an exception. The default for the timeout parameter is 5.0 (five seconds).

(https://docs.python.org/3/library/sqlite3.html#connection-objects) 因此,只要“写入”正在进行,只读语句将无法执行。因此,我会为每个写操作打开一个新连接。

其次,对于基于文件的数据库(如 SQLite),打开连接的成本显然比网络访问的数据库低得多。你可以剖析它;一个简单的测试,如:

    x = 0
    for i in range(1000):
        async with aiosqlite.connect('pic_db.db') as db:
            async with db.execute('SELECT 12', ()) as cursor:
                x += 1

显示这两个语句总共耗时约 550 毫秒;如果 forconnect 互换,需要 ~150ms;所以每个 connect 花费你大约 0.4ms。如果这是一个问题,我会尝试为只读语句重用一个连接(并为每个修改语句打开一个新连接)。