我有一些数据库结构;因为大部分与我们无关,所以我只描述一些相关的部分。以lake Item对象为例:
items_table = Table("invtypes", gdata_meta, Column("typeID", Integer, primary_key = True), Column("typeName", String, index=True), Column("marketGroupID", Integer, ForeignKey("invmarketgroups.marketGroupID")), Column("groupID", Integer, ForeignKey("invgroups.groupID"), index=True)) mapper(Item, items_table, properties = {"group" : relation(Group, backref = "items"), "_Item__attributes" : relation(Attribute, collection_class = attribute_mapped_collection('name')), "effects" : relation(Effect, collection_class = attribute_mapped_collection('name')), "metaGroup" : relation(MetaType, primaryjoin = metatypes_table.c.typeID == items_table.c.typeID, uselist = False), "ID" : synonym("typeID"), "name" : synonym("typeName")})
我想在sqlalchemy/database层实现一些性能改进,并有一些想法: 1) 两次请求同一物品:
^{pr2}$每个请求生成并发出SQL查询。为了避免这种情况,我对一个item对象使用了两个自定义映射:
itemMapId = {} itemMapName = {} @cachedQuery(1, "lookfor") def getItem(lookfor, eager=None): if isinstance(lookfor, (int, float)): id = int(lookfor) if eager is None and id in itemMapId: item = itemMapId[id] else: item = session.query(Item).options(*processEager(eager)).get(id) itemMapId[item.ID] = item itemMapName[item.name] = item elif isinstance(lookfor, basestring): if eager is None and lookfor in itemMapName: item = itemMapName[lookfor] else: # Items have unique names, so we can fetch just first result w/o ensuring its uniqueness item = session.query(Item).options(*processEager(eager)).filter(Item.name == lookfor).first() itemMapId[item.ID] = item itemMapName[item.name] = item return item
我相信sqlalchemy也做类似的对象跟踪,至少通过主键(项目编号). 若确实如此,我可以擦除这两个映射(尽管擦除名称映射将需要对使用这些查询的应用程序进行小的修改),以避免重复功能和使用常用方法。实际的问题是:如果sqlalchemy中有这样的功能,如何访问它?在
2)快速加载关系通常有助于将大量请求保存到数据库。例如,我肯定需要以下一组item=item()属性:
item.group (Group object, according to groupID of our item) item.group.items (fetch all items from items list of our group) item.group.items.metaGroup (metaGroup object/relation for every item in the list)
如果我有一些条目ID,但是还没有加载条目,我可以从数据库中请求它,急切地加载我需要的一切:sqlalchemy将在单个查询中联接group、它的条目和相应的元组。如果我使用默认的延迟加载访问它们,sqlalchemy将需要发出1个查询来获取item+1以获取列表中所有项的group+1*#items+1*#items以获取每个项的元组,这是浪费。在
2.1)但是如果我已经获取了Item对象,并且我想要加载的一些属性已经加载了呢?据我所知,当我从数据库中重新获取某个对象时,它已经加载的关系不会被卸载,对吗?在
2.2)如果我已经获取了Item对象,并且想访问它的组,我可以使用项目组ID,应用我需要的任何急切的陈述(“项目”和“项目.元组"). 它应该正确地加载组和它所请求的关系,而不需要接触项目内容。sqlalchemy会正确地将这个获取的组映射到项目组,这样当我访问项目组它不会从底层数据库中获取任何内容?在
2.3)如果我从数据库中提取了以下内容:原始项,项目组还有一部分item.group.items项列出其中一些可能已经加载了元组,完成数据结构的最佳策略与上面的eager list相同:使用(“items”重新获取组项目.元组“)紧急加载,或者分别检查项目列表中的每个项目,如果项目或其元组未加载-加载它们?这似乎取决于具体情况,因为如果所有的东西都在一段时间前就已经加载了,那么发出如此繁重的查询是毫无意义的。sqlalchemy是否提供了一种跟踪某个对象关系是否已加载的方法,并且能够深入查看一个级别吗?在
作为2.3的一个例子-我可以获取ID为83的组,急切地获取“items”和项目.元组". 有没有办法从一个条目(groupID为83)中确定它是否有“group”组.项“和”group.items.metaGroup组“是否加载,使用sqlalchemy工具(在本例中,应该加载所有工具)?在
要强制加载lazy属性,只需访问它们。这是最简单的方法,它对关系很好,但是对于
Column
s(您将为同一表中的每一列获得单独的SQL查询)效率低下。您可以从sqlalchemy.orm.attributes.instance_state(obj).unloaded
获取所有已卸载属性(关系和列)的列表。在示例中没有使用延迟列,但为了完整起见,我将在这里描述它们。处理延迟列的典型方案如下:
deferred()
装饰选定的列。通过使用group
参数将它们组合成一个或多个组。在undefer()
和undefer_group()
选项。在不幸的是,这并不起相反的作用:您可以将列组合成组,而不必使用
column_property(Column(…), group=…)
来延迟加载它们,但是defer()
选项不会影响它们(它只适用于Column
s,而不是列属性,至少在0.6.7中是这样)。在要强制加载延迟列属性,
session.refresh(obj, attribute_names=…)
由nathanvilaescusa建议的可能是最好的解决方案。我看到的唯一缺点是它首先使属性过期,因此必须确保作为attribute_names
参数传递的属性中没有加载的属性(例如,使用与state.unloaded
的交集)。在更新
1)SQLAlchemy会跟踪加载的对象。ORM就是这样工作的:对于每个标识,会话中必须只有一个对象。默认情况下,它的内部缓存是弱的(使用
weak_identity_map=False
来更改它),因此只要代码中没有对该对象的引用,就会从缓存中删除该对象。当对象已经在会话中时,SQLAlchemy不会对query.get(pk)
执行SQL请求。但这只适用于get()
方法,因此query.filter_by(id=pk).first()
将在会话中使用加载的数据执行SQL请求和刷新对象。在2)急切地加载关系将导致更少的请求,但并不总是更快。你必须检查你的数据库和数据。在
2.1)从数据库重取数据不会卸载通过关系绑定的对象。在
2.2)
item.group
是使用query.get()
方法加载的,因此如果对象已经在会话中,则不会导致SQL请求。在2.3)是的,视情况而定。在大多数情况下,最好是希望SQLAlchemy使用正确的策略:)。对于已经加载的关系,您可以检查相关对象的关系是否通过
state.unloaded
以递归方式加载到任何深度。但是,当关系尚未加载时,您无法知道相关对象及其关系是否已加载:即使关系尚未加载,相关对象也可能已在会话中(想象一下,您先请求项,加载其组,然后请求具有相同组的其他项)。对于您的特定示例,我认为递归地检查state.unloaded
没有问题。在(一) 从Session documentation:
2.1)您是正确的,刷新对象时不会修改关系。在
2.2)是的,小组将出现在身份图中。在
2.3)我相信你最好的选择是重新装载整个组.项在单个查询中。根据我的经验,发出一个大的请求通常比几个较小的请求快得多。唯一有意义的是只重新加载特定的组.项是否确实有一个需要装载。虽然在这种情况下,您只执行一个大的查询而不是一个小的查询,所以您实际上并没有减少查询的数量。在
我没有尝试过,但是我相信您应该能够使用sqlalchemy.orm.util.identity_key方法来确定对象是否在sqlalchemy的identiy映射中。我想知道调用identiy\u key(Group,83)返回什么。在
初始问题) 如果我理解正确的话,你有一个从数据库中获取的对象,它的一些关系是预先加载的,你想用一个查询来获取其余的关系?我相信您可以使用Session.refresh()方法传递要加载的关系的名称。在
相关问题 更多 >
编程相关推荐