sql - 使用 LIMIT 进行慢速查询(Active Record `first` 方法)

标签 sql ruby-on-rails postgresql activerecord

我观察到在大表上运行 Model.where(*condition with 3 integer indexes*).first 会花费太多时间。

first 添加按 id 排序,因此它可能应该对所有 150 万条记录进行排序:

Game.where(private: 0, status: 0).first
DEBUG -- :   Game Load (__1278.6ms__)
SELECT  "games".* FROM "games" WHERE "games"."private" = 0 AND "games"."status" = 0
__ORDER BY "games"."id" ASC__ LIMIT 1

删除 first 会使事情变得更快:

Game.where(private: 0, status: 0)
DEBUG -- :   Game Load (__68.0ms__)
SELECT "games".* FROM "games" WHERE "games"."private" = 0 AND "games"."status" = 0

但是,如果我手动删除排序,事情仍然不会那么快:

Game.where(private: 0, status: 0).order(nil).first
DEBUG -- :   Game Load (__323.7ms__)
SELECT  "games".* FROM "games" WHERE "games"."private" = 0 AND "games"."status" = 0 LIMIT 1

有人知道这是什么原因吗?现在我考虑使用 scope.to_a.first 这似乎要快得多。

第一个查询的解释计划是:

1. Limit (cost=0.43..59.68 rows=1 width=59)
2. -> Index Scan using games_pkey on games (cost=0.43..90007.49 rows=1519 width=59)
3. Filter: ? 

UPD

这很奇怪,但今天我看到了第二个查询的其他结果(现在它几乎是立即执行的):

Game.where(private: 0, status: 0).order(nil).first
DEBUG -- :   Game Load (__2.5ms__)
SELECT  "games".* FROM "games"  WHERE "games"."private" = 0 AND "games"."status" = 0 LIMIT 1

最佳答案

如果不发布查询计划的任何细节,就很难调试问题。可能有几个因素导致查询暂时变慢,这些因素与代码本身无关,而是与数据库存储数据的方式有关。

仅依靠查询的执行时间可能无法真正揭示查询是否高效。

一般来说,如果涉及排序条件,LIMIT 需要更多资源,因为数据库必须在内部对数据进行排序才能提取 N 条记录。当然,如果sort子句中使用的属性没有索引,那么查询效率会更低。

ActiveRecord 公开了firsttake。如果你跑

Game.where(private: 0, status: 0).first

然后 ActiveRecord 将按主键对记录进行排序(除非您指定排序列),而如果您使用

Game.where(private: 0, status: 0).take

ActiveRecord 会查询数据库,只获取第一个。哪种解决方案更好,这取决于。在第二种情况下,结果是不可预测的,因为数据库将以它想要的任何顺序返回数据。

通常,应用排序条件的成本非常低。但同样,您需要检查查询计划。

在控制台中,只需附加 .explain 即可转储特定查询的查询计划。例如

puts Game.where(private: 0, status: 0).explain
puts Game.where(private: 0, status: 0).order(:id).explain

关于sql - 使用 LIMIT 进行慢速查询(Active Record `first` 方法),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32229615/

相关文章:

sql - 如何在 SELECT (T-SQL) 中使用变量

ruby-on-rails - 在 Rails 环境下使用 Rspec 测试 Rake 任务

php - 使用 PHP 将 MySQL 和 Postgres 数据库中的数据放入单个 HTML 表中

php - 使用 LIKE 在 MySQL php 中检索逗号分隔值

php - 获取一天的总工作时间、总工作时间、总外出时间 mysql

sql - XACT_ABORT 并不总是在出错时回滚事务。它什么时候做?

ruby-on-rails - Rails - Postgres - 在类型转换之前提取属性

ruby-on-rails - 防止多次点击创建多条记录 - Rails

sql - postgres 选择聚合时间跨度

java - 将 PostgreSQL Query 转换为对应的 Hibernate Query