我发现跑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
答案 0 :(得分:1)
如果不发布查询计划的任何细节,则很难调试该问题。可能有几个因素导致查询暂时减慢,这些因素与代码本身无关,而与数据库存储数据的方式有关。
仅仅依赖查询的执行时间可能无法真实地揭示查询是否有效。
一般来说,如果涉及排序条件,LIMIT
需要更多资源,因为数据库必须在内部对数据进行排序才能提取N条记录。当然,如果sort子句中使用的属性没有编入索引,那么查询效率会更低。
ActiveRecord公开了first
和take
。如果你运行
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