Django 过滤器图标在 TextField() 上计数缓慢

Posted

技术标签:

【中文标题】Django 过滤器图标在 TextField() 上计数缓慢【英文标题】:Django Filter icontains count slow on TextField() 【发布时间】:2020-04-30 06:52:49 【问题描述】:
response_title = models.TextField(null=True,blank=True)
response_status_code = models.IntegerField(null=True,blank=True)
response_body = models.TextField(null=True,blank=True)

最近我的网站一直面临性能缓慢的问题,所以这是我从 Django shell 观察到的,我的模型中有 32k 个条目,与icontains 相比,containscounticontains 上的执行速度很慢查询耗时 4 秒,而 count 上的 contain 耗时 0.3 秒。

我在response_body 中存储的数据类型是原始响应正文。

from .models import Response_Dataset

>>> Response_Dataset.objects.count() ## 0.1 sec
32289

>> Response_Dataset.objects.filter(response_body__icontains='hack') ## 0.4 seconds

>>> x = Response_Dataset.objects.filter(response_body__icontains='hack')
>>> x.count() ### 4 seconds
65

>>> x = Response_Dataset.objects.filter(response_body__contains='a') ### 0.2 seconds
>>> x.count() ### 0.3 seconds
23857 

response_body 之外的任何其他字段上使用icontains 执行速度非常快,例如在response_titleresponse_status_code

【问题讨论】:

关于你最后的陈述,response_body 不是有很多文本,所以进行文本匹配查找自然会更慢吗? 是的,response_body 确实会比 response_title 更大,但我认为 count() 不需要以秒为单位 会的。表中的行数和response_body内容大小在这里起着关键作用。 你建议我用什么来更快地在大型内容行中查找 【参考方案1】:

    您需要知道 Django 查询集何时真正从数据库中获取结果。在您真正需要该值之前,Django ORM 不会访问数据库。在Django documentation中有详细描述。

    在内部,一个 QuerySet 可以被构造、过滤、切片,并且通常在不实际访问数据库的情况下传递。在您对查询集进行评估之前,实际上不会发生任何数据库活动。

from .models import Response_Dataset

>>> Response_Dataset.objects.count()  # ==> Database Hit
32289

>>> x = Response_Dataset.objects.filter(response_body__icontains='hack') # ==> Doesn't hit
>>> x.count() # ==> Hit
65

>>> x = Response_Dataset.objects.filter(response_body__contains='a') # ==> Doesn't hit
>>> x.count() # ==> Hit
23857 

    如果某些SQL语句很慢,则需要查看数据库中的EXPLAIN SELECT语句并适当设置Index。您可以安装 Django 调试工具栏并使用debugsqlshell command 来查看数据库请求的 SQL。优化方法取决于您使用的 DBMS。

    在我看来,如果你想在一个非常大的数据集上进行全文搜索,像 ElasticSearch 这样的搜索引擎是正确的选择。

【讨论】:

我正在考虑使用 FTS,我认为 Postgres 已内置支持 vectorfield,但我真的不确定它会占用多少空间。 如果数据库在云环境中,我认为最准确的基准是恢复到数据库的备份一段时间,看看自ALTER TABLE以来空间发生了多少变化。强烈建议您在进行基准测试时注意不要连接到您的生产环境。创建新的连接信息时很容易出错。祝你好运:)

以上是关于Django 过滤器图标在 TextField() 上计数缓慢的主要内容,如果未能解决你的问题,请参考以下文章

在 Django 过滤器中加入两个模型

如何在 django 模板中过滤 object_list

字段错误在/。无法将关键字“textField”解析为字段

django queryset过滤外键

Django:按计算字段过滤

django - 改进迭代查询