草庐IT

mysql - 如何提高 Django 管理搜索相关字段的查询性能 (MySQL)

coder 2023-10-08 原文

在 Django 中我有这个:

模型.py

class Book(models.Model):
    isbn = models.CharField(max_length=16, db_index=True)
    title = models.CharField(max_length=255, db_index=True)
    ... other fields ...

class Author(models.Model):
    first_name = models.CharField(max_length=128, db_index=True)
    last_name = models.CharField(max_length=128, db_index=True)
    books = models.ManyToManyField(Book, blank=True)
    ... other fields ...

admin.py

class AuthorAdmin(admin.ModelAdmin):
    search_fields = ('first_name', 'last_name', 'books__isbn', 'books__title')

    ...

我的问题是,当我使用 2 个或更多短期术语从作者管理列表页面进行搜索时,MySQL 开始花费大量时间(对于 3 个术语查询至少需要 8 秒)。我有大约 5000 位作者和 2500 本书。这里的非常重要。如果我搜索“a b c”,那么 3 个非常短的术语,我就没有足够的耐心等待结果(我至少等了 2 分钟)。相反,如果我搜索“所有蜜蜂线索”,我会在 2 秒内得到结果。所以这个问题看起来确实与相关领域的短期术语有关。

此搜索产生的 SQL 查询有很多 JOIN、LIKE、AND 和 OR 但没有子查询。

我正在使用 MySQL 5.1,但我尝试使用 5.5 但没有成功。

我还尝试将 innodb_buffer_pool_size 增加到一个非常大的值。这没有任何改变。

我现在提高性能的唯一想法是对 isbntitle 字段进行非规范化(即将它们直接复制到 Authors 中),但我必须添加一堆机制使这些字段与 Book 中的真实字段保持同步。

关于如何改进这个查询有什么建议吗?

最佳答案

经过大量调查,我发现问题出在如何为管理搜索字段(在 ChangeList 类中)构建搜索查询。在多词搜索(由空格分隔的单词)中,每个词都通过链接一个新的 filter() 添加到 QuerySet。当 search_fields 中有一个或多个相关字段时,创建的 SQL 查询将有很多 JOIN 一个接一个地链接着许多 JOIN对于每个相关领域(请参阅我的 related question 了解一些示例和更多信息)。这条 JOIN 链在那里,因此每个术语将仅在数据过滤器的子集中由先行术语搜索,并且最重要的是,相关字段只需要一个术语(与需要have ALL terms) 进行匹配。参见 Spanning multi-valued relationships在 Django 文档中获取有关此主题的更多信息。我很确定这是管理员搜索字段大部分时间想要的行为。

此查询(涉及相关字段)的缺点是性能(执行查询的时间)的变化可能非常大。这取决于很多因素:搜索词的数量、搜索的词、字段搜索的种类(VARCHAR 等)、字段搜索的数量、表中的数据、表的大小等。使用正确的组合很容易拥有一个几乎永远需要花费的查询(一个需要超过 10 分钟的查询。对我来说,在这个搜索字段的上下文中是一个需要永远花费的查询)。

之所以会花费这么长时间,是因为数据库需要为每个术语创建一个临时表,并对其进行大部分扫描以搜索下一个术语。所以,这加起来真的很快。

为提高性能可能要做的更改是对同一 filter() 中的所有术语进行 AND 运算。这样,它们将只有一个 JOIN 相关字段(如果是多对多,则为 2 个),而不是更多。这个查询会快很多并且性能变化非常小。缺点是相关字段必须匹配所有术语,因此,在许多情况下,您可以获得较少的匹配项。

更新

正如 trinchet 所问,这是更改搜索行为所需的内容(针对 Django 1.7)。您需要覆盖您希望进行此类搜索的管理类的 get_search_results()。您需要将基类 (ModelAdmin) 中的所有方法代码复制到您自己的类中。然后你需要改变这些行:

for bit in search_term.split():
    or_queries = [models.Q(**{orm_lookup: bit})
                  for orm_lookup in orm_lookups]
    queryset = queryset.filter(reduce(operator.or_, or_queries))

对此:

and_queries = []
for bit in search_term.split():
    or_queries = [models.Q(**{orm_lookup: bit})
                  for orm_lookup in orm_lookups]
    and_queries.append(Q(reduce(operator.or_, or_queries)))
queryset = queryset.filter(reduce(operator.and_, and_queries))

此代码未经测试。我的原始代码是针对 Django 1.4 的,我只是在此处针对 1.7 对其进行了调整。

关于mysql - 如何提高 Django 管理搜索相关字段的查询性能 (MySQL),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9669807/

有关mysql - 如何提高 Django 管理搜索相关字段的查询性能 (MySQL)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  9. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  10. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

随机推荐