【问题标题】:Minimizing the performance issues of loading a many to many relationship最小化加载多对多关系的性能问题
【发布时间】:2014-02-17 17:45:50
【问题描述】:

我一直在标记一个非常大的语料库。每个 Unigram 可以多次出现在多个评论中。我将 Comment.ids 存储在附加到数据库中 Unigram 的列表中,每 25 万个新计算的 unigrams 左右。我想知道是否有一种方法可以扩展评论 id 列表(或类似的数据结构),而无需查询和加载与 Unigram 相关的现有 cmets 列表(它的数量可以达到数千个)。还是没有办法绕过缓慢的 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)

以及添加新计数和 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)

【问题讨论】:

  • 你能澄清一下... database every 250K newly counted unigrams ...吗?您的意思是该列表每增加约 250K 个新 unigram 就更新一次?您还可以谈谈数据集,它是实时数据集还是静态数据集(?)。通常这样的东西会变成一个稀疏矩阵,你不这样做有什么原因吗?
  • @MattiLyra 这是一个静态数据集。当我标记了 ~250K unigram 时,我会将计数从内存传输到数据库。在内存中,我将它们散列到一个字典中,其中键是 unigram,值是另一个包含出现计数和 comment_ids 的字典。从关于稀疏矩阵的 wiki 文章中,我认为字典属于该范围。

标签: python sql orm many-to-many flask-sqlalchemy


【解决方案1】:

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

relationship() 的默认行为是完全加载集合 ... 中的项目数 集合就是所谓的“动态”关系。这是 relationship() 的一种可选形式,它在访问时返回一个 Query 对象来代替集合。

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

occurs_in = db.relationship('Comment', secondary=comments, 
    lazy='dynamic', backref=db.backref('unigrams'))

occurs_in = db.relationship('Comment', secondary=comments, 
    lazy='dynamic', backref=db.backref('unigrams', lazy='dynamic'))

另外,您也可以改用lazy='noload'。由于您只是在索引期间写入表,因此这将是相同的。

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

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

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

如果出于某种原因(为什么?)您真的必须在 SQL 数据库中放置文本索引,那么您可以省去很多麻烦,并且不要使用像 SQLAlchemy 这样的对象关系映射器。最好的方法是,以合适的格式(作为文本文件)准备数据转储,然后一次性将其加载到数据库中(在 MySQL 中使用 LOAD DATA INFILE 之类的东西,或在数据库中使用类似的东西)。这要快几个数量级。它可以很容易地达到为每个 unigram 运行单个 INSERT 查询的速度的 1000 倍。您仍然可以稍后通过 SQLAlchemy 访问数据,前提是您以正确的方式组织表格,但是在为文本编制索引时,您希望绕过它。

【讨论】:

    猜你喜欢
    • 2023-03-08
    • 2016-03-24
    • 1970-01-01
    • 2014-08-31
    • 2013-04-28
    • 2011-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多