asyncpg - 连接 vs 连接池

23

我正在阅读 asyncpg 的文档,但我无法理解为什么要使用连接池而不是单个连接。

给出的示例中,使用了连接池:

async with pool.acquire() as connection:
    async with connection.transaction():
        result = await connection.fetchval('select 2 ^ $1', power)
        return web.Response(
            text="2 ^ {} is {}".format(power, result))

但也可以在需要时创建连接来完成:

connection = await asyncpg.connect(user='postgres')
async with connection.transaction():
    result = await connection.fetchval('select 2 ^ $1', power)
    return web.Response(
            text="2 ^ {} is {}".format(power, result))

使用池而不是连接的优点是什么?

2个回答

43

连接到数据库服务器是一项昂贵的操作。连接池是一种常见的技术,可以避免支付这个费用。池保持连接处于打开状态,并在必要时出租它们。

通过进行简单的基准测试,很容易看出连接池的好处:

async def bench_asyncpg_con():
    power = 2
    start = time.monotonic()
    for i in range(1, 1000):
        con = await asyncpg.connect(user='postgres', host='127.0.0.1')
        await con.fetchval('select 2 ^ $1', power)
        await con.close()

    end = time.monotonic()
    print(end - start)

在我的电脑上,以上操作只需要1.568秒。

然而,池版本是:

async def bench_asyncpg_pool():
    pool = await asyncpg.create_pool(user='postgres', host='127.0.0.1')
    power = 2
    start = time.monotonic()
    for i in range(1, 1000):
        async with pool.acquire() as con:
            await con.fetchval('select 2 ^ $1', power)

    await pool.close()
    end = time.monotonic()
    print(end - start)

运行时间为0.234秒,或者 比原来快6.7倍


你建议在使用像pgPool这样的工具时,仍然使用客户端连接池吗? - Fulvio Esposito
我得到了6.5秒和0.75秒(在虚拟机中运行),但也有趣的是:1000个 pool.acquire() 耗时0.6秒,1000个 con.fetchval() 耗时0.15秒。所以 acquire() 看起来比 fetchval() 贵4倍,或者我想错了? - davidtgq
1
此外,连接池处理重新连接,而使用单个连接则需要自己处理重新连接。 - liberforce
@davidtgq 你可以尝试增加 min_sizemax_size 的值。acquire() 方法会在可用的连接上运行查询之前进行锁定。因此,如果所有的连接都被占用,它会等待至少有一个连接空闲。这样等待的时间比实际工作本身所需的时间更长。你需要调整这些参数以更好地适应你的工作负载。 - winwin

2

Elvis Pranskevichus展示了两个基准测试。我选取了第一个bench_asyncpg_con并进行了编辑,将con移出循环:

async def bench_asyncpg_con_2():
    power = 2
    start = time.monotonic()
    con = await asyncpg.connect(**data)
    for i in range(1, 1000):
        await con.fetchval('select 2 ^ $1', power)

    await con.close()
    end = time.monotonic()
    print(end - start) 

这比 bench_asyncpg_pool 更快。

我在这个测试中得到了 0.45 的结果,而在 bench_asyncpg_pool 测试中得到了

1.62,在 bench_asyncpg_con 测试中得到了 63.18

我对使用这个库还是新手,认为一个连接 con 对于整个项目来说是一个不错的选择。如果我错了,请纠正我。非常感谢。


6
如果您的应用程序只运行一个查询,单个连接就够了。然而,大多数使用数据库的应用程序也同时处理多个事项(例如通过 Web 服务器为不同客户端的多个请求)。在这些情况下,单个连接很快成为瓶颈,但为每个请求创建新连接也很昂贵。这就是为什么要使用连接池的原因。如果您的真实程序也是纯顺序处理的话,那么单个连接就可以了。 - AVee

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