使用LIMIT缓慢查询(Active Record`first`方法)

时间:2015-08-26 14:39:08

标签: sql ruby-on-rails postgresql activerecord

我发现跑Model.where(*condition with 3 integer indexes*).first会占用大桌子上的太多时间。

firstid添加排序,因此可能需要对所有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

1 个答案:

答案 0 :(得分: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