为啥 QuerySet 迭代这么慢?

Posted

技术标签:

【中文标题】为啥 QuerySet 迭代这么慢?【英文标题】:why is QuerySet iteration so slow?为什么 QuerySet 迭代这么慢? 【发布时间】:2018-07-22 16:08:50 【问题描述】:

我正在尝试为系统创建一个准确且高效的搜索算法。我安装了 Postgresql 以利用其三元相似度查询,这就是我搜索对象的方式:

objects_found = Question.objects.extra(where=["CHAR_LENGTH(answer) > 300"])).filter(question__trigram_similar=message

这非常快,执行大部分查询不到 0.5 秒。 objects_found queryset 的所有对象都与查询文本相似,但我需要找出最相似的一个。

我知道有两种算法在这种情况下非常好,第一种是Cosine Similarity,第二种是Ratcliff/Obershelp pattern recognition(which has built-in implementation in Python)。

我尝试进行迭代,对它们中的每一个进行测试,在大多数情况下,余弦相似度大约快 1.5 倍(正如预期的那样,考虑到向量的测量速度要快得多),但 SequenceMatcher 会给出更准确的结果。所以我还是选择了SequenceMatcher。请注意,此迭代花费了很长时间。

最后,我尝试在代码中实现 SequenceMatcher:

objects_found = (Question.objects.extra(where=["CHAR_LENGTH(answer) > 300"])).filter(question__trigram_similar=message).iterator()
zsim = ("", 0)
for i in objects_found:
    rsim = _search.ratcliff_obershelp(querytext, i.question)
    if zsim[1] < rsim:
       zsim = (i.answer, rsim)
       if rsim > 0.75:  # works in most of the cases
            break
response = zsim[0]

数据库中有约 1GB 约 500 万行,postgresql 只需不到 0.5 秒即可选择具有三元相似性的正确行。在大约 500 万行中,只有 10-90 行被过滤,查询集迭代大约需要 62 秒才能找到最相似的行。

即使迭代在开始时中断也是如此,例如,如果只有 4 行要迭代以达到 75% 以上的相似度,Django 仍会加载 90 行。

我真的怀疑相似度算法本身是否存在问题,它似乎只是查询集需要很长时间来加载行,一旦它们被加载,算法几乎可以立即完成所有事情。

为什么会这样?有什么方法可以让 Queryset 迭代更省时吗?数据库级迭代会产生更快的结果吗?

p.s 时间由 python 的时间模块测量。

【问题讨论】:

@Evert 我从 python 的 time 库中对其进行了计时。说t1 是执行Question.objects.extra(where=["CHAR_LENGTH(answer) &gt; 300"])).filter(question__trigram_similar=message 之前的时间,而t2 是之后的时间:然后我计算了t2 - t1 @Evert 为误解道歉,这不是使迭代器内存高效吗?但我想知道,它会影响时间效率吗? @Evert 是的,这就是我所指的。 Django 将 ORM 调用转换为 SQL 以在需要时执行。尝试计时list(objects_found),以评估查询集。 看看queryset API。它会回答你到目前为止的所有问题。我将充实我的评论并将其作为答案发布,因为这似乎是您的问题。 【参考方案1】:

您所面临的困惑是由 Django 对QuerySets 的延迟评估引起的。您等待 Django 的 0.5 秒实际上只是准备 SQL - 也就是说,将 ORM 调用转换为 SQL 查询(或多个 SQL 查询),以便稍后执行。

QuerySets 被评估 as late as possible 然后缓存,所以要找出它通过 Django 实际花费的时间,您需要强制评估 QuerySet,以让 Django 执行 SQL。您可以通过以下几种方式执行此操作,例如:

print(objects_found)

list(objects_found)

for item in objects_found:
    pass

【讨论】:

以上是关于为啥 QuerySet 迭代这么慢?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Dictionary.First() 这么慢?

为啥通过 django QuerySet 进行查询比在 Django 中使用游标慢得多?

Django 2021年最新版教程21数据库查询 model 多条数据 queryse转dict字典 返回渲染到前端

Django QuerySet 包含重复的条目

为啥迭代置换生成器比递归慢?

为啥wordpress反应这么慢