3

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

我这里有它的解释分析:

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)

更新

我在这里看到在可以忽略索引的布尔字段上添加索引...我认为这是正确的,因为与测试用户相比,非测试用户实际上相当多。

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

4 回答 4

3

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

我在这篇博文中用更多的细节和例子描述了它们。

于 2013-06-20T12:06:45.030 回答
2

如果您SET enable_seqscan = off(仅出于测试目的,不要postgresql.conf在生产中设置或使用它,因为它会使其他查询大大变慢)并重新测试,您可能会发现在强制使用索引时false情况会变慢.

就我个人而言,我会删除索引,而是在(is_test) WHERE (NOT is_test).

如果这是一种常见的模式,我也会考虑让其他大量使用的索引部分使用WHERE (NOT is_test),因为它会大大加快非测试索引的使用。

无论如何,如果SET enable_seqscan = off情况更快(不太可能),那么您的速度random_page_cost可能太高了。

此外,如果您使用的是 PostgreSQL 9.2,您可能会针对真实情况获得更好的计划;它通常能够使用仅索引扫描来完全避免扫描表。如果索引相对于表足够小并且真空运行足够积极,它甚至可能对错误情况使用仅索引扫描,因为它必须读取的数据要少得多。由于您的表格非常宽(90 个字段),这似乎很有可能。所以考虑升级。

于 2013-06-20T12:21:10.033 回答
1

这似乎很正常......根据行数,真实值产生大约 10% 的行;false 产生剩余的 90%。在后一种情况下,读取整个表比跟随索引来回读取要快。(它的选择性不够有用。)

于 2013-06-20T12:08:13.107 回答
0

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

如果扫描这么多数据需要 9 秒,那么要么您的服务器严重超载,要么数据没有缓存,必须从磁盘读取。

您可以通过打开 track_io_timing 获得更好的信息,然后使用“解释(分析,缓冲区)选择 ....”重做查询。

于 2013-06-20T22:12:51.047 回答