【问题标题】:MySQL executes some ActiveRecord queries slower than if run directly (via "mysql" command)MySQL 执行一些 ActiveRecord 查询比直接运行慢(通过“mysql”命令)
【发布时间】:2019-03-05 14:44:56
【问题描述】:

我有一个奇怪的 ActiveRecord 行为(可能与 MySQL 服务器一起使用)有一个巨大的表(数亿个条目)如果我进行这个简单的调用:

SearchResult.where(id: ids[0..15000]).select('uid').to_a

...这将花费不到 1 秒的时间。

现在如果我打这个电话:

SearchResult.where(id: ids[0..16000]).select('uid').to_a

...可能需要几分钟!

同时,如果我运行以下两个查询以获取所有 16k 条目 - 它总共在 1 秒内顺利运行:

SearchResult.where(id: ids[0..15000]).select('uid').to_a +
  SearchResult.where(id: ids[15001..16000]).select('uid').to_a

此外,如果我在长时间运行的命令上调用 .to_sql 并直接通过 mysql 命令或 MySQL Workbench 执行它 - 甚至不到一秒钟。

我运行了 ANALYZE TABLE,然后直接和通过 ActiveRecord 尝试了 EXPLAIN,它们是相同的。两者虽然都使用ALL scan(全表扫描),唯一的区别是直接执行的很好,但是通过ActiveRecord执行的时候就挂了。现在有趣的是 - 如果我强制使用 PRIMARY 索引 - 它开始通过 ActiveRecord 正常工作,但这不是一个解决方案,因为我不能将它与 ActiveRecord 关联预加载一起使用。

这种奇怪的行为真的让我很困扰,因为我使用了大量的 ActiveRecord 关联预加载(例如 .includes(searches: :search_results)),有时最终会请求成千上万条记录(是的,我确实需要所有记录)等等查询停止执行好几分钟。我只会强制使用索引,但它不能解决其他地方的问题,会在其他地方造成更多问题,而且我将无法使用 ActiveRecord 预加载。有什么想法吗?

【问题讨论】:

  • ids 数组由什么组成?重新考虑您的查询模式以使用子查询或范围可能更有意义。一个Array 值将创建一个IN 子句,一个Range 值将创建一个BETWEEN 子句,使用不同的ActiveRecord::Relation 将创建一个子查询。

标签: mysql ruby-on-rails activerecord


【解决方案1】:

也许不是 SQL,而是 SearchResult 实例的实例化。
如果您只对uid 感兴趣,请尝试

SearchResult.where(id: ids[0..16000]).pluck(:uid)

这会返回一个 uids 数组,而不是一个 SearchResults 数组。

【讨论】:

  • @neolancer 这也是我想说的。没有其他原因需要时间 - MySQL(或任何其他数据库)不关心它从哪里接收请求(这是连接级别的详细信息,执行引擎通常更深)。所以实例化一定是原因。
  • 另外请注意ids[0..16000] 将创建一个新的Array,其中可能包含多达16_000 个元素b) 一个IN 子句一英里长(例如IN(1,2,3,4,5,...,16000)),这也很可怕并且可能值得重新考虑查询模式。
  • 同意,但这不是我的意思。我可能举了一个错误的例子。 pluck 产生相同的性能问题。即使我通过 ActiveRecord::Base.connection.execute("select uid from search_results where id in (...)") 执行裸 SQL - 16k id 仍然很慢,15k id 很快。似乎在某些时候有太多 id 或什么
  • @engineersmnky 好吧,这就是 ActiveRecord 在预加载关联时的工作方式。而且我相信 MySQL 处理长查询应该没有问题。我确实同意这会产生大量查询。给定服务器上有足够的 max_allowed_pa​​cket 选项,这应该不是问题,除非我错过了其他东西
  • @neolancer 还有其他方法是我的观点,正如我在您的问题下的评论中所指出的那样。 IN 通常比使用 between 或使用子查询的已知范围要慢。尽管我必须同意这个答案,但在这种情况下,性能下降很可能是由对象创建开销引起的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-16
  • 2015-07-24
  • 2013-08-14
  • 1970-01-01
  • 1970-01-01
  • 2012-06-20
相关资源
最近更新 更多