Python-GINO中的Baked Queries性能优化指南

Python-GINO中的Baked Queries性能优化指南

gino python-gino/gino: 是一个基于 Python 的 ORM 框架,支持异步 I/O 和 PostgreSQL、MySQL、SQLite 等多种数据库。该项目提供了一个简单易用的 API,可以方便地实现数据库的查询和操作,同时支持多种数据库和异步 I/O。 gino 项目地址: https://gitcode.com/gh_mirrors/gi/gino

什么是Baked Queries

Baked Queries是Python-GINO提供的一种查询性能优化技术,它通过预编译SQL语句和缓存查询对象构造过程来显著提升频繁执行查询的性能。根据官方测试,使用Baked Queries可以使查询执行速度提升至少40%。

为什么需要Baked Queries

在传统ORM中,每次执行查询通常需要完成以下步骤:

  1. 构造查询对象
  2. 编译查询为SQL语句
  3. 准备语句(对于支持预编译的数据库)
  4. 执行查询

对于频繁执行的相同查询,前三个步骤实际上是重复工作。Baked Queries通过缓存这些步骤的结果,避免了重复计算,从而提高了查询效率。

两种使用方式

1. 底层Bakery API

这种方式适合需要精细控制查询的场景:

import gino
import sqlalchemy as sa

# 创建Bakery实例
bakery = gino.Bakery()

# 烘焙原生SQL查询
db_time = bakery.bake("SELECT now()")

# 烘焙带参数的查询
user_query = bakery.bake("SELECT * FROM users WHERE id = :uid")

# 使用SQLAlchemy Core烘焙查询
metadata = sa.MetaData()
user_table = sa.Table(
    "users", metadata,
    sa.Column("id", sa.Integer, primary_key=True),
    sa.Column("name", sa.String),
)
user_query = bakery.bake(
    sa.select([user_table]).where(user_table.c.id == sa.bindparam("uid"))
)

# 创建带Bakery的引擎
engine = await gino.create_engine("postgresql://localhost/", bakery=bakery)

# 执行烘焙查询
now = await engine.scalar(db_time)
user = await engine.first(user_query, uid=123)

2. 高级Gino集成

这种方式更适合常规应用开发,与GINO ORM深度集成:

from gino import Gino

db = Gino()

class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)

# 烘焙简单查询
db_time = db.bake("SELECT now()")

# 烘焙模型查询
user_getter = db.bake(User.query.where(User.id == db.bindparam("uid")))

async def main():
    async with db.with_bind("postgresql://localhost/"):
        # 执行查询
        print(await db_time.scalar())
        user = await user_getter.first(uid=1)
        print(user.name)

模型类中的Baked Queries

可以将常用查询直接定义为模型类方法:

class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)

    @db.bake
    def getter(cls):
        return cls.query.where(cls.id == db.bindparam("uid"))

    @classmethod
    async def get(cls, uid):
        return await cls.getter.one_or_none(uid=uid)

这种方式使代码组织更加清晰,查询逻辑与模型紧密关联。

自定义加载器

可以在烘焙查询时指定加载选项:

# 方式1:直接在查询中指定
user_getter = db.bake(
    User.query.where(User.id == db.bindparam("uid")).execution_options(
        loader=User.load(comment="Added by loader.")
    )
)

# 方式2:通过bake参数指定
user_getter = db.bake(
    User.query.where(User.id == db.bindparam("uid")),
    loader=User.load(comment="Added by loader."),
)

# 方式3:使用装饰器
@db.bake(loader=User.load(comment="Added by loader."))
def user_getter():
    return User.query.where(User.id == db.bindparam("uid"))

执行时也可以临时覆盖加载器:

user = await user_getter.load(User).first(uid=1)

可用API

BakedQuery继承了GinoExecutor的所有方法,包括:

  • all() - 获取所有结果
  • first() - 获取第一个结果
  • one() - 获取唯一结果(无结果或多结果会报错)
  • one_or_none() - 获取零或一个结果
  • scalar() - 获取标量结果
  • status() - 获取执行状态
  • load() - 指定加载器
  • timeout() - 设置超时

延迟准备语句

默认情况下,GINO会在创建引擎时为所有烘焙查询在所有连接上创建预编译语句。如果表结构尚未创建(如依赖db.gino.create_all()),可以禁用此行为:

# 使用底层API
e = await gino.create_engine("postgresql://...", bakery=bakery, prebake=False)

# 使用GINO集成
await db.set_bind("postgresql://...", prebake=False)

这样预编译语句会在首次执行查询时按需创建并缓存。

最佳实践

  1. 对于频繁执行的查询,特别是性能敏感的核心业务逻辑,优先考虑使用Baked Queries
  2. 将常用查询封装为模型类方法,提高代码复用性
  3. 在应用启动阶段完成所有查询的烘焙工作
  4. 根据实际场景选择是否启用prebake
  5. 合理使用加载器优化数据加载过程

通过合理使用Baked Queries,可以显著提升GINO应用的数据库查询性能,特别是在高并发场景下效果更为明显。

gino python-gino/gino: 是一个基于 Python 的 ORM 框架,支持异步 I/O 和 PostgreSQL、MySQL、SQLite 等多种数据库。该项目提供了一个简单易用的 API,可以方便地实现数据库的查询和操作,同时支持多种数据库和异步 I/O。 gino 项目地址: https://gitcode.com/gh_mirrors/gi/gino

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

章迅筝Diane

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值