2017-01-10 50 views
0

我运行简单的查询:的PostgreSQL和ActiveRecord的 - 慢查询

History.where(channel_id: 1).order('histories.id DESC').first 

结果:

History Load (808.8ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 

808.8ms 7 1的记录与CHANNEL_ID = 1。总的历史数为2110443。

如果我选择CHANNEL_ID所有历史= 1:

History.where(channel_id: 1) 

History Load (0.5ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 [["channel_id", 1]] 

只花了0.5ms的

如果我们试图采取一个纪录红宝石阵列的帮助:

History.where(channel_id: 1).order('histories.id DESC').to_a.first 

History Load (0.5ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY id DESC [["channel_id", 1]] 

我应该在哪里找到问题?

PS:我已经有了channel_id字段的索引。

UPD:

History.where(channel_id: 1).order('histories.id DESC').limit(1).explain 
    History Load (848.9ms) SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 
=> EXPLAIN for: SELECT "histories".* FROM "histories" WHERE "histories"."channel_id" = 1 ORDER BY histories.id DESC LIMIT 1 [["channel_id", 1]] 
               QUERY PLAN 
------------------------------------------------------------------------------------------------------- 
Limit (cost=0.43..13.52 rows=1 width=42) 
    -> Index Scan Backward using histories_pkey on histories (cost=0.43..76590.07 rows=5849 width=42) 
     Filter: (channel_id = 1) 
(3 rows) 
+1

通常,'channel_id,id'复合索引应该有所帮助。如果你的用例是特殊的,即你只需要通道'1',你可以设置一个[部分索引](https://www.postgresql.org/docs/current/static/indexes-partial.html) 。 – pozs

+0

@pozs谢谢你,通过复合索引'channel_id,id'解决 – zolter

回答

0

有两种方法的PostgreSQL可以处理你的查询(与ORDER BY和LIMIT子句):

  • 它可以扫描表,并责令发现的元组,然后限制你的结果。如果PostgreSQL认为你的表的元组数量非常少,或者他认为索引不起作用,PostgreSQL将选择这个计划;
  • 它可以使用索引。

看来,PostgreSQL的做选择第一个选项,它可以发生在我的愚见只有两个原因:

  • 你的表的统计信息是不准确的。我建议你真空你的表,然后再试一次这个查询
  • channel_id值是不均匀分布的(例如几乎所有的元组都有channel_id = 2),这就是为什么PostgreSQL认为索引是没用的原因。这里我建议使用部分索引。