只读对象模型的SqlAlchemy优化

时间:2010-02-23 23:01:10

标签: python performance sqlalchemy readonly

我使用sqlalchemy ORM映射从sqlite数据库中生成了一个复杂的对象网络。我有很多深层嵌套:

for parent in owner.collection: 
    for child in parent.collection: 
        for foo in child.collection: 
            do lots of calcs with foo.property 

我的分析表明,sqlalchemy仪器在这个用例中花了很多时间。

问题是:我不会在运行时更改对象模型(映射属性),因此一旦加载它我就不需要仪器,或者根本不需要任何sqlalchemy开销。经过大量的研究,我想我可能必须从我已加载的“仪表化对象”中克隆一组“纯python”对象,但这将是一种痛苦。

性能在这里非常重要(它是一个模拟器),所以也许最好使用sqlite api将这些层写为C扩展。有什么想法吗?

3 个答案:

答案 0 :(得分:9)

如果您多次引用单个实例的单个属性,一个简单的技巧就是将其存储在局部变量中。

如果您想要一种创建廉价纯python克隆的方法,请与原始对象共享dict对象:

class CheapClone(object):
    def __init__(self, original):
        self.__dict__ = original.__dict__

创建这样的副本大约有一半的检测属性访问和属性查找成本与正常情况一样快。

可能还有一种方法可让映射器创建未经检测的类的实例,而不是已检测的类。如果我有一些时间,我可以看看根深蒂固的假设是填充的实例与检测类的类型相同。


发现一种快速而肮脏的方式,似乎至少在0.5.8和0.6上有所作为。没有使用继承或其他可能相互影响的功能进行测试。此外,这涉及一些非公共API,因此在更改版本时要小心破损。

from sqlalchemy.orm.attributes import ClassManager, instrumentation_registry

class ReadonlyClassManager(ClassManager):
    """Enables configuring a mapper to return instances of uninstrumented 
    classes instead. To use add a readonly_type attribute referencing the
    desired class to use instead of the instrumented one."""
    def __init__(self, class_):
        ClassManager.__init__(self, class_)
        self.readonly_version = getattr(class_, 'readonly_type', None)
        if self.readonly_version:
            # default instantiation logic doesn't know to install finders
            # for our alternate class
            instrumentation_registry._dict_finders[self.readonly_version] = self.dict_getter()
            instrumentation_registry._state_finders[self.readonly_version] = self.state_getter()

    def new_instance(self, state=None):
        if self.readonly_version:
            instance = self.readonly_version.__new__(self.readonly_version)
            self.setup_instance(instance, state)
            return instance
        return ClassManager.new_instance(self, state)

Base = declarative_base()
Base.__sa_instrumentation_manager__ = ReadonlyClassManager

用法示例:

class ReadonlyFoo(object):
    pass

class Foo(Base, ReadonlyFoo):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)
    name = Column(String(32))

    readonly_type = ReadonlyFoo

assert type(session.query(Foo).first()) is ReadonlyFoo

答案 1 :(得分:0)

你应该能够对有问题的关系禁用延迟加载,sqlalchemy将在一个查询中获取它们。

答案 2 :(得分:-1)

尝试使用JOIN而不是python循环的单个查询。