如何使用Sqlalchemy ORM执行单查询更新及效率对比
问题1:第一种常规写法是否产生两次数据库请求?
是的,该写法会产生两次数据库往返:
- 第一次是执行
session.query(Dummy).filter(Dummy.id == 1).first()时发送的SELECT查询,用于获取目标ORM实例 - 第二次是执行
session.commit()时发送的UPDATE语句,提交属性变更
如果在修改属性和commit之间执行过session.flush(),UPDATE会提前到flush阶段发送,本质还是两次请求。
问题2:第二种update写法是否效率更高?
是的,第二种写法效率明显更高。
该写法会直接生成对应的UPDATE语句发送到数据库,全程只需要1次数据库往返,没有前置的SELECT查询,单条更新的性能就有优势,如果是批量更新场景,性能差距会非常大。
但要注意两个默认限制:
- 该操作属于bulk级操作,不会触发ORM实例级的before_update、after_update等监听器,也不会自动处理实例的关联关系同步
- 如果当前session中已经缓存了id=1的Dummy实例,该操作不会自动更新缓存中的实例属性,后续再从session中读取该实例会拿到旧值,需要手动调用
session.expire(dummy_instance)或者session.refresh(dummy_instance)刷新缓存。
问题3:需要触发ORM监听器的场景如何处理?
分两种场景选择方案:
单条/小批量更新场景
如果更新量级很小,直接使用第一种常规写法即可,两次请求的开销可以忽略,能天然兼容所有ORM监听器逻辑。如果需要避免并发更新冲突,可以在查询时加行锁:
dummy = session.query(Dummy).filter(Dummy.id == 1).with_for_update().first() dummy.name = "dumb-dumb" session.commit()
大量数据更新场景
如果需要兼顾更新性能和监听器逻辑,可以选择以下两种方案:
- 改用会话级/语句级事件监听:不使用实例级的update监听器,改为监听
before_execute或after_execute事件,匹配对应的UPDATE语句后执行自定义逻辑,该类监听器对bulk update、Core update语句都生效,不需要加载ORM实例,也不会产生额外查询。 - 若必须使用实例级监听器,可以先批量查询出所有需要更新的实例(1次SELECT),遍历修改属性后统一commit(批量UPDATE会优化为更少的请求),相比单条两次请求的写法,性能也会有明显提升。
内容的提问来源于stack exchange,提问作者Walrus
相关产品推荐
相关产品推荐

