最大限度地减少加载多对多关系的性能问题

weg*_*gry 6 python sql orm many-to-many flask-sqlalchemy

我一直在想象一个非常大的语料库.每个Unigram可以多次出现在多个注释中.我将Comment.ids存储在一个列表中,该列表附加到数据库中的Unigram,每250K新计算的unigrams左右.我想知道的是,如果有一种方法可以扩展注释ID列表 - 或类似的数据结构 - 而无需查询和加载与Unigram绑定的现有注释列表(它可以在数千个中编号).或者缓慢的IO没有办法吗?

这是我的型号代码:

comments = db.Table('ngrams',
    db.Column('unigram_id', db.String, db.ForeignKey('comment.id')),
    db.Column('comment_id', db.String, db.ForeignKey('unigram.id')))

class Unigram(db.Model):
    id = db.Column(db.String, primary_key=True, unique=True)
    times_occurred = db.Column(db.Integer)
    occurs_in = db.relationship('Comment', secondary=comments,
                    backref=db.backref('unigrams', lazy='dynamic'))

class Comment(db.Model):
    id = db.Column(db.String, primary_key=True, unique=True)
    creation_time = db.Column(db.DateTime)
Run Code Online (Sandbox Code Playgroud)

以及在以下位置添加新计数和Comment.ids的代码:

current = Unigram.query.filter(Unigram.id == ngram).first()
if current:
    current.times_occurred += counts[ngram]['count']
    current.occurs_in.extend(counts[ngram]['occurences'])
else:
    current = Unigram(ngram, counts[ngram]['count'],
                  counts[ngram]['occurences'])
    db.session.add(current)
Run Code Online (Sandbox Code Playgroud)

Ale*_*x I 4

您的具体问题的答案(我认为):\n http://docs.sqlalchemy.org/en/rel_0_7/orm/collections.html#dynamic-relationship-loaders

\n\n
\n

关系()的默认行为是完全加载\n中的项目集合...启用大型集合管理的一个关键功能是所谓的\xe2\x80\x9cdynamic\xe2\x80\x9d关系。这是关系()的一种可选形式,它在访问时返回一个查询对象来代替集合。

\n
\n\n

看起来 SQLAlchemy 确实支持不必读取集合来修改它。所以lazy=\'dynamic\'是正确的。问题可能在于您仅在 backref 上有它。尝试这两个变体:

\n\n
occurs_in = db.relationship(\'Comment\', secondary=comments, \n    lazy=\'dynamic\', backref=db.backref(\'unigrams\'))\n\noccurs_in = db.relationship(\'Comment\', secondary=comments, \n    lazy=\'dynamic\', backref=db.backref(\'unigrams\', lazy=\'dynamic\'))\n
Run Code Online (Sandbox Code Playgroud)\n\n

另外,你也可以尝试lazy=\'noload\'一下。由于您只是在索引期间写入表,因此这将起到相同的作用。

\n\n

现在,对于更广泛的问题:为什么要这样做?即使你解决了这个小问题,这样做也会令人沮丧。一些想法...

\n\n

使用正确的工具来完成工作:Sphinx、ElasticSearch、Lucene、Solr、Xapian,其中任何一种都可以非常彻底地处理文本索引问题,并且比不使用专门工具处理它要好得多。Sphinx 的执行速度特别快,索引速度为每秒数百兆字节,查询有多少文档包含一个单词通常需要一两毫秒(无论语料库大小如何)。

\n\n

如果您正在编写一次性脚本或测试代码,而不是设置生产系统,并且由于某种原因不想使用正确的工具,那么全部在内存中完成,并且不要使用 SQL 。在 python 中使用普通字典,并在运行之间将它们保存为 ramdisk 上的 pickle 文件。买更多的内存,比你的时间便宜。这是在文本语料库上测试统计思想的好方法。

\n\n

如果出于某种原因你确实必须在 SQL 数据库中放置文本索引(为什么?),那么可以省去很多麻烦,并且不要使用像 SQLAlchemy 这样的对象关系映射器。执行此操作的最佳方法是,以合适的格式(作为文本文件)准备数据转储,然后将其一次性加载到数据库中(使用类似LOAD DATA INFILEMySQL 的工具或数据库中的等效工具)。这要快几个数量级。它可以轻松地将每个一元组运行单独 INSERT 查询的速度提高 1000 倍。如果您以正确的方式组织表,您稍后仍然可以通过 SQLAlchemy 访问数据,但在为文本建立索引时您希望绕过它。

\n