Flask API中SQLAlchemy ORM与原生SQL查询性能差异及优化咨询
在Flask API的POST路由数据验证逻辑中,给目标模型新增列属性后API响应变慢,尝试用仅查询必要列的原生SQL优化,结果却比原SQLAlchemy ORM查询更慢。核心疑问有两个:
- 为何此处ORM查询更快?
- 如何通过SQLAlchemy ORM实现仅查询指定列的逻辑(或不新增列的关联查询)?
一、ORM查询更快的核心原因
对比两段代码的查询逻辑,性能差距主要来自以下几点:
查询复杂度差异
ORM代码中BuyModel.query.filter_by(id=payload_buy_id).one()是单表查询,直接从buy表获取数据;而原生SQL做了三次表JOIN(buy→plan→brief),多表关联的IO开销远大于单表查询,自然更慢。从代码能看出,ORM中直接访问buy_record.brief_id、buy_record.client_id,说明你已经给BuyModel新增了这两个列,无需关联其他表即可获取数据。Session缓存与复用差异
ORM大概率复用了Flask-SQLAlchemy提供的全局Session(比如db.session),SQLAlchemy会对Session内的查询结果做缓存;而原生SQL每次调用create_pg_session()新建Session,无法复用缓存,重复查询时会重复执行SQL语句。查询执行的优化差异
SQLAlchemy ORM会自动生成经过优化的SQL语句,且能利用数据库的主键索引(buy.id是主键,查询必然走索引);而原生SQL的JOIN逻辑如果没有合理利用索引(比如关联字段未建索引),会进一步放大性能差距。
二、用ORM实现指定列查询的方案
方案1:仅查询BuyModel的指定列(适合已新增列的场景)
如果已经给BuyModel新增了brief_id、client_id列,响应变慢的核心原因是查询了所有列(包括新增的大字段),可以用with_entities指定仅查询验证所需的字段:
# SQLAlchemy 1.x写法 buy_record = BuyModel.query.with_entities( BuyModel.id, BuyModel.plan_id, BuyModel.brief_id, BuyModel.client_id ).filter_by(id=payload_buy_id).one() # SQLAlchemy 2.0+推荐写法 from sqlalchemy import select stmt = select( BuyModel.id, BuyModel.plan_id, BuyModel.brief_id, BuyModel.client_id ).where(BuyModel.id == payload_buy_id) buy_record = db.session.execute(stmt).one()
这样查询只会获取指定的4个列,不会加载其他新增的冗余字段,大幅减少数据传输和IO开销。
方案2:不新增列,通过关联查询获取数据(还原原生SQL逻辑)
如果不想给BuyModel新增列,而是通过关联表获取brief_id、client_id,可以用ORM的JOIN+指定列查询实现原生SQL的逻辑:
假设模型关系定义为:
class BuyModel(db.Model): id = db.Column(db.Integer, primary_key=True) plan_id = db.Column(db.Integer, db.ForeignKey('plan.id')) plan = db.relationship('Plan') class Plan(db.Model): id = db.Column(db.Integer, primary_key=True) brief_id = db.Column(db.Integer, db.ForeignKey('brief.id')) brief = db.relationship('Brief') class Brief(db.Model): id = db.Column(db.Integer, primary_key=True) client_id = db.Column(db.Integer)
查询代码可以写为:
# SQLAlchemy 1.x写法 buy_data = BuyModel.query.join(BuyModel.plan)\ .join(Plan.brief)\ .with_entities( BuyModel.id, BuyModel.plan_id, Plan.brief_id, Brief.client_id ).filter(BuyModel.id == payload_buy_id)\ .one() # SQLAlchemy 2.0+推荐写法 stmt = select( BuyModel.id, BuyModel.plan_id, Plan.brief_id, Brief.client_id ).join(Plan, BuyModel.plan_id == Plan.id)\ .join(Brief, Plan.brief_id == Brief.id)\ .where(BuyModel.id == payload_buy_id) buy_data = db.session.execute(stmt).one()
这种方式和原生SQL逻辑完全一致,但能利用ORM的缓存、索引优化等特性,性能会比手写原生SQL更稳定。
内容的提问来源于stack exchange,提问作者maximosis

