导读:本期聚焦于菲律宾程序员创作的《如何使用asyncpg高效连接PostgreSQL?Python异步数据库编程实战指南》,敬请观看详情。数据库连接耗时是Python后端服务常见的性能瓶颈,当系统并发请求量上来之后,传统的同步驱动会让接口响应明显变慢。asyncpg是专为PostgreSQL设计的高性能异步驱动库,它直接实现了PostgreSQL二进制协议,跳过了Python层面的类型转换开销,查询速度比 psycopg2 快出不少。本文将围绕asyncpg的核心用法展开,包括连接池的创建与配置、参数化查询防止SQL注入、事务与异步上下文管理器的配合、以及和FastAPI等Web框架整合的完整示例,同时也会分析常见报错场景和性能调优建议,帮助你写出高吞吐、低延迟的数据库访问层代码。

在Python的数据库生态里,操作PostgreSQL最经典的方案是psycopg2,但它是同步阻塞的,一个查询没执行完,整个事件循环都会被卡住。asyncpg的出现解决了这个痛点,它是专门为PostgreSQL打造的高性能异步驱动,官方实测性能比psycopg2和aiopg都要出色。这篇文章会从安装、连接池、查询、事务到与Web框架整合,完整讲清楚asyncpg的用法和容易踩的坑。

如何使用asyncpg高效连接PostgreSQL?Python异步数据库编程实战指南

为什么选择asyncpg而不是psycopg2或aiopg

asyncpg最大的特点是直接实现了PostgreSQL的二进制协议,而不是在C驱动或者DB-API之上做封装。这意味着数据在传输阶段就已经是PostgreSQL的原生格式,asyncpg在Python层直接解析,省去了字符串转换的中间环节。对于返回大量行的查询,这个优势非常明显,尤其是数值类型和时间类型,不需要逐行做类型推断和转换。

与aiopg相比,asyncpg不是简单地在psycopg2外面套一层线程池,而是真正的原生异步实现。aiopg的并发能力受限于线程池大小,而asyncpg完全运行在事件循环里,配合uvloop使用时性能还能进一步提升。此外,asyncpg内置了对Prepared Statement的支持,同一个SQL多次执行时会复用执行计划,减少解析开销。

安装非常简单,直接用pip即可:

pip install asyncpg

需要说明的是,asyncpg只支持PostgreSQL,不支持其他数据库。如果你的项目未来可能切换数据库,这会是一个需要权衡的点,可以选择SQLAlchemy 2.0的异步版本作为抽象层,底层仍然用asyncpg驱动。

创建连接与连接池的正确姿势

单次连接适合脚本场景,直接调用asyncpg.connect()就能拿到一个连接对象:

import asyncio
import asyncpg

async def main():
    # 单连接示例
    conn = await asyncpg.connect(
        host='127.0.0.1',
        port=5432,
        user='postgres',
        password='secret',
        database='demo',
    )
    # 执行简单查询
    version = await conn.fetchval('SELECT version()')
    print(version)
    await conn.close()

asyncio.run(main())

但在实际的Web服务中,绝对不要每次请求都新建连接。建立连接的过程涉及TCP握手、认证、会话初始化,成本很高。正确的做法是使用连接池,asyncpg提供了create_pool函数:

import asyncpg

pool = None

async def init_pool():
    global pool
    pool = await asyncpg.create_pool(
        host='127.0.0.1',
        port=5432,
        user='postgres',
        password='secret',
        database='demo',
        min_size=5,        # 池中常驻连接数
        max_size=20,       # 最大连接数
        command_timeout=30, # 单条命令超时时间
    )

async def query_users():
    async with pool.acquire() as conn:
        rows = await conn.fetch('SELECT id, name FROM users')
        return rows

连接池的acquire方法配合async with使用,连接用完会自动归还,即使代码抛出异常也不会泄漏连接。建议连接池在应用启动时创建一次,全局复用,不要在函数内部反复创建,否则池就失去了意义。

还有一点需要注意,max_size不要设置得过大。PostgreSQL每个连接都是一个进程,连接太多会消耗大量内存,一般按照服务的并发量乘以单请求的持有时长来估算即可,通常20到50已经能支撑大多数业务。

参数化查询与结果处理

asyncpg使用$1$2这样的占位符做参数化查询,这一点和psycopg2的%s风格完全不同,从psycopg2迁移代码时必须改写:

async def get_user(pool, user_id):
    async with pool.acquire() as conn:
        # 使用 $1 占位符,防止SQL注入
        row = await conn.fetchrow(
            'SELECT id, name, email FROM users WHERE id = $1', user_id
        )
        return row

async def insert_user(pool, name, email):
    async with pool.acquire() as conn:
        # returning 返回插入后的数据
        new_id = await conn.fetchval(
            'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id',
            name, email
        )
        return new_id

查询结果有三种主要方法:fetch返回Record对象列表,fetchrow返回单条或None,fetchval返回第一行第一列的值。Record对象既支持索引访问也支持键访问,可以像字典一样用row['name']取值,也能通过dict(row)转成字典。

一个常见的坑是字段值为NULL时的处理。asyncpg对类型推断非常严格,如果参数中要传入NULL,最好显式指定类型,例如$1::int,否则可能报类型推断失败的错误:

# 显式类型转换,避免 None 导致类型推断失败
row = await conn.fetchrow(
    'SELECT * FROM users WHERE age = $1::int', None
)

另外,asyncpg不会自动把Python的datetime转成带时区的timestamp,如果字段是timestamptz类型,传入的datetime对象必须带时区信息,否则会抛出类型错误,这是新手最容易遇到的问题之一。

事务管理与实战整合

事务是数据库操作绕不开的话题,asyncpg提供了多种方式开启事务,最推荐的是用async with conn.transaction()这种上下文管理器形式,代码块正常结束自动提交,抛出异常自动回滚:

async def transfer_points(pool, from_user, to_user, points):
    async with pool.acquire() as conn:
        async with conn.transaction():
            # 事务内的多条SQL,要么全部成功,要么全部回滚
            await conn.execute(
                'UPDATE users SET points = points - $1 WHERE id = $2',
                points, from_user
            )
            await conn.execute(
                'UPDATE users SET points = points + $1 WHERE id = $2',
                points, to_user
            )

如果需要更细粒度的控制,可以使用conn.transaction()返回的对象手动调用startcommit,但在异步代码中手动管理事务边界容易出错,上下文管理器是更安全的选择。

最后看一下与FastAPI整合的完整例子,在应用生命周期中管理连接池:

from contextlib import asynccontextmanager
from fastapi import FastAPI
import asyncpg

@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.pool = await asyncpg.create_pool(
        host='127.0.0.1', user='postgres',
        password='secret', database='demo',
        min_size=5, max_size=20
    )
    yield
    await app.state.pool.close()

app = FastAPI(lifespan=lifespan)

@app.get('/users/{user_id}')
async def get_user(user_id: int):
    pool = app.state.pool
    async with pool.acquire() as conn:
        row = await conn.fetchrow(
            'SELECT id, name, email FROM users WHERE id = $1', user_id
        )
    if row is None:
        return {'error': 'user not found'}
    return {'id': row['id'], 'name': row['name'], 'email': row['email']}

这种写法保证了连接池随应用启动而创建、随应用关闭而释放,各个请求处理函数直接访问app.state.pool,干净且高效。

总结一下,asyncpg的核心优势在于原生异步加二进制协议带来的高性能,使用时的要点是:全局复用连接池、坚持参数化查询、用上下文管理器处理事务、注意NULL参数和时区字段的类型问题。把这些细节处理好,你的Python服务在数据库层面就不会成为瓶颈。

asyncpgPostgreSQLPython异步编程修改时间:2026-09-13 20:48:52

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260913/56236.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。