postgres:索引未被使用或如何优化

时间:2013-06-20 11:46:26

标签: postgresql query-optimization

我有一个带有布尔字段索引的表(“test”)。当它是真的时,它使用索引,因此它的加载速度很快但是当它为假时它不会使用它。有什么不对吗?

我在这里解释它的分析:

DB_development=# explain analyze SELECT COUNT(*) FROM "users" WHERE "users"."is_test" = 't';
                                                                      QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=22890.67..22890.68 rows=1 width=0) (actual time=1848.655..1848.656 rows=1 loops=1)
   ->  Index Scan using index_users_on_is_test on users  (cost=0.00..22846.51 rows=17665 width=0) (actual time=34.727..1844.081 rows=21457 loops=1)
         Index Cond: (is_test = true)
         Filter: is_test
 Total runtime: 1848.882 ms
(5 rows)

DB_development=# explain analyze SELECT COUNT(*) FROM "users" WHERE "users"."is_test" = 'f';
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=84505.74..84505.75 rows=1 width=0) (actual time=9557.632..9557.632 rows=1 loops=1)
   ->  Seq Scan on users  (cost=0.00..84063.72 rows=176807 width=0) (actual time=71.653..9533.595 rows=219531 loops=1)
         Filter: (NOT is_test)
 Total runtime: 9557.655 ms
(4 rows)

更新

我在这里看到Adding an index on a boolean field它可以忽略索引......我认为这是正确的,因为与测试用户相比,非测试用户实际上相当多。

DB_development=# SELECT COUNT(*) FROM "users" WHERE "users"."is_test" = 't';
 count
-------
 21457
(1 row)

DB_development=# SELECT COUNT(*) FROM "users" WHERE "users"."is_test" = 'f';
 count
--------
 219531
(1 row)

如果是这样的话......我怎么能快速计算呢?

更新

这是创建表和索引:

  create_table "users", :force => true do |t|
    t.integer  "genre_id"
    t.integer  "country_id"
    t.boolean  "is_test",                                          :default => false
    t.datetime "created_at"
    t.datetime "updated_at"

    ... + 90 more fields (it's my main table)
  end

  add_index "users", ["country_id"], :name => "index_users_on_country_id"
  add_index "users", ["genre_id"], :name => "index_users_on_genre_id"
  add_index "users", ["is_test"], :name => "index_users_on_is_test"

  ... + 17 more indexes

4 个答案:

答案 0 :(得分:2)

索引可能无法使用的原因有很多。表太小了。列/值组合不够有选择性。 PostgreSQL“认为”扫描另一种方式会更快。

我在this blogpost中用更多细节和示例描述了它们。

答案 1 :(得分:2)

如果您SET enable_seqscan = off(仅用于测试目的,请勿在{{1​​}}中设置或在生产中使用它,因为它会使其他查询大幅减慢)并重新测试,您将会当被迫使用索引时,可能会发现postgresql.conf情况较慢

就个人而言,我会删除索引,而是在false上添加部分索引。

如果这是一种常见模式,我还会考虑将其他频繁使用的索引部分放在(is_test) WHERE (NOT is_test)上,因为它会大大加快非测试索引的使用速度。

无论如何,如果WHERE (NOT is_test)案例更快(相当不可能),那么SET enable_seqscan = off可能会过高。

另外,如果你使用PostgreSQL 9.2,你可能会得到一个更好的真实案例计划;它通常能够使用仅索引扫描来避免扫描表格。如果索引相对于表足够小并且真空运行得足够激烈,它甚至可能仅对错误情况使用仅索引扫描,因为它必须读取这么少的数据。由于你的桌子非常宽(90个字段),这似乎很有可能。所以考虑升级。

答案 2 :(得分:1)

这似乎完全正常......根据行计数,真实值产生大约10%的行;假产生剩余的90%。在后一种情况下,读取整个表比在索引之后来回读取更快。 (它没有足够的选择性,有用。)

答案 3 :(得分:0)

假设您的费用参数设置为默认值,您的表格大约为84063.72-1768页,或640 MB。 (用\ d +验证)

如果需要9秒钟来扫描那么多数据,那么您的服务器会非常过载,或者数据没有被缓存,必须从磁盘读取数据。

您可以通过启用track_io_timing然后使用“explain(analyze,buffers)select”重做查询来获得更好的信息。