一个用于sqlalchemy的大容量延迟加载程序,它解决了n+1加载问题

sqlalchemy-bulk-lazy-loader的Python项目详细描述


一个用于sqlalchemy关系的自定义惰性加载程序,它确保关系始终有效加载。此加载程序自动解决n+1 query problem问题,而无需手动向所有查询添加joinedloadsubqueryload语句

问题

每当在循环中延迟加载关系时,就会出现n+1查询问题例如:

students=session.query(Student).limit(100).all()forstudentinstudents:print('{} studies at {}'.format(student.name,student.school.name))

在上面的代码中,将生成101个sql查询-一个用于加载学生列表,然后为每个学生生成100个单独查询以加载该学生的学校。声明如下:

SELECT*FROMstudentsLIMIT100;SELECT*FROMschoolsWHEREschools.student_id=1LIMIT1;SELECT*FROMschoolsWHEREschools.student_id=2LIMIT1;SELECT*FROMschoolsWHEREschools.student_id=3LIMIT1;SELECT*FROMschoolsWHEREschools.student_id=4LIMIT1;...

这很糟糕

用sqlalchemy解决这个问题的传统方法是在初始查询中添加一个joinedloadsubqueryload,以包括学校和学生,如下所示:

students=(session.query(Student).options(subqueryload(Student.school)).limit(100).all())forstudentinstudents:print('{} studies at {}'.format(student.name,student.school.name))

虽然这样做有效,但它需要添加到执行的每个查询中,并且当添加了许多相关模型时,您可以很容易地得到大量的subqueryloadjoinedload列表。如果您在任何地方都忘记了一个,那么您将静默地返回到n+1查询问题。另外,如果以后不再需要关系,则需要记住从原始查询中删除它,否则现在加载的数据太多。此外,在任何有数据库查询的地方,都必须在代码中维护这些相关模型的列表,这是一个巨大的痛苦。

如果您不必担心添加subqueryloadjoinedload,而且还可以保证所有关系都能有效加载,这不是很好吗

批量延迟加载程序的工作原理

99%的情况下,如果内存中加载了一个模型列表,并且其中一个模型上的关系是延迟加载的,那么您就处于循环中,并且将在每个其他模型上请求相同的关系。sqlalchemy bulk lazy loader假设是这种情况,并且每当模型上的关系被延迟加载时,它将在当前会话中查找需要加载相同关系的任何其他类似模型,并将发出一个单独的批量sql语句来一次加载它们。

这意味着您可以在循环中加载所需的所有关系,同时确保所有关系都以性能方式加载,并且只加载所使用的关系。例如,上面的代码相同:

students=session.query(Student).limit(100).all()forstudentinstudents:print('{} studies at {}'.format(student.name,student.school.name))

大容量延迟加载程序将只发出2条sql语句,就像您在初始查询中指定了subqueryload一样,只是现在您的代码更干净了,而且您可以保证只加载所需的关系。耶!

安装

SQLAlchemy Bulk Lazy Loader可以通过pip安装

pip install SQLAlchemy-bulk-lazy-loader

用法

在声明SQLAlchemy映射之前,需要运行以下命令:

fromsqlalchemy_bulk_lazy_loaderimportBulkLazyLoaderBulkLazyLoader.register_loader()

这将向sqlalchemy注册加载程序,并通过在关系映射中指定lazy='bulk'使其在关系上可用。例如:

classStudent(db.model):id=db.Column(db.Integer,primary_key=True)school_id=db.Column(db.Integer,db.ForeignKey('school.id'))classSchool(db.model):id=db.Column(db.Integer,primary_key=True)students=db.relationship('Student',lazy='bulk',backref=db.backref('school',lazy='bulk'))

就这样!批量延迟加载程序将用于student.schoolschool.students关系。

限制

目前只支持单个主键或简单辅助联接上的关系。

students=relationship('Student',lazy='bulk')# OK!students=relationship('Student',lazy='bulk',order_by=Student.id)# OK!student=relationship('Student',lazy='bulk',uselist=False)# OK!students=relationship('Student',lazy='bulk',secondary=school_to_students)# OK!students=relationship('Student',lazy='bulk',secondary=school_to_students,primaryjoin='and_(...)')# NOT SUPPORTED

不支持Python2。

但我有一个案例,我想用不同的方式加载关系!

如果要在查询中加载仍使用subqueryloadjoinedload的关系,您仍然可以这样做-只有在尚未加载的模型上请求关系时,批量延迟加载程序才会启动。如果在特定情况下确实需要对关系加载进行细粒度控制,还可以使用attributes.set_committed_value(model, <relation_name>, <related_model/s>)显式设置相关模型。实际上,BulkLazyLoader就是这样在幕后工作的。

贡献

欢迎投稿!创建一个请求并确保添加测试覆盖率测试使用sqlalchemy测试框架,可以使用py.test运行。

装载愉快!

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java如何将jaxb插件扩展与gradlejaxbplugin一起使用   java Hibernate列表<Object[]>到特定对象   java使用多态性显示arraylist的输出   java水平堆叠卡,带有一定偏移量   java错误:找不到符号方法liesInt()   java客户机/服务器文件收发中的多线程流管理   在java中可以基于访问重载方法吗?   包含空元素的java排序数组   swing Java按钮/网格布局   java BottomNavigationView getmaxitemcount   java空指针异常字符串生成器   java Xamarin升级导致“类文件版本错误52.0,应为50.0”错误   java我正在尝试打印它,而不只是对每一行进行println   Tomcat7中的java是否需要复制上下文。将xml转换为conf/Catalina/locahost以使其生效   带有注入服务的java REST端点在何处引发自定义WebServiceException?   在Java中使用GPS数据   java如何将JFreeChart ChartPanel导出到包含添加的CrosshairOverlay的图像对象?   内置Eclipse期间的Java 8堆栈溢出   java在GWT编译的JavaScript中如何表示BigDecimal