【问题标题】:PostgreSQL Bitmap Heap Scan on index is very slow but Index Only Scan is fastPostgreSQL Bitmap Heap Scan on index 非常慢,但 Index Only Scan 很快
【发布时间】:2014-10-31 02:42:59
【问题描述】:

我创建了一个有 43kk 行的表,用值 1..200 填充它们。因此,表格中每个数字大约 220k。

create table foo (id integer primary key, val bigint);
insert into foo
  select i, random() * 200 from generate_series(1, 43000000) as i;
create index val_index on foo(val);
vacuum analyze foo;
explain analyze select id from foo where val = 55;

结果: http://explain.depesz.com/s/fdsm

我希望总运行时间

如果我使用仅索引扫描,它的工作速度非常快:

explain analyze select val from foo where val = 55;

http://explain.depesz.com/s/7hm

但我需要选择 id 而不是 val 所以仅 Incex 扫描不适合我的情况。

提前致谢!

附加信息:

SELECT relname, relpages, reltuples::numeric, pg_size_pretty(pg_table_size(oid)) 
FROM pg_class WHERE oid='foo'::regclass;

结果:

"foo";236758;43800000;"1850 MB"

配置:

"cpu_index_tuple_cost";"0.005";""
"cpu_operator_cost";"0.0025";""
"cpu_tuple_cost";"0.01";""
"effective_cache_size";"16384";"8kB"
"max_connections";"100";""
"max_stack_depth";"2048";"kB"
"random_page_cost";"4";""
"seq_page_cost";"1";""
"shared_buffers";"16384";"8kB"
"temp_buffers";"1024";"8kB"
"work_mem";"204800";"kB"

【问题讨论】:

  • 您能否在问题中包含此查询的输出:SELECT relname,relpages,reltuples::numeric,pg_size_pretty(pg_table_size(oid)) FROM pg_class WHERE oid='foo'::regclass;
  • 请为您的两个查询执行EXPLAIN (analyze, buffers)。并包含此查询的输出:SELECT name,setting,unit FROM pg_settings WHERE source NOT IN ('default','override') UNION ALL SELECT 'version',version(),NULL;
  • 从您提供的查询中添加了配置并更新了解释链接(现在包含缓冲区信息)

标签: sql performance postgresql


【解决方案1】:

我在这里得到了答案: http://ask.use-the-index-luke.com/questions/235/postgresql-bitmap-heap-scan-on-index-is-very-slow-but-index-only-scan-is-fast

诀窍是对 id 和 value 使用复合索引:

create index val_id_index on foo(val, id);

因此将使用仅索引扫描,但我现在可以选择 id

select id from foo where val = 55;

结果:

http://explain.depesz.com/s/nDt3

但这仅适用于 9.2+ 版本的 Postgres。 如果您已强制使用以下版本,请尝试其他选项。

【讨论】:

    【解决方案2】:

    虽然您只查询了表的 0.5%,或价值约 10MB 的数据(在将近 2GB 的表中),但感兴趣的值均匀分布在整个表中。

    您可以在您提供的第一个计划中看到它:

    • BitmapIndexScan 在 123.172 毫秒内完成
    • BitmapHeapScan 耗时 17055.046 毫秒。

    您可以尝试根据索引顺序对表进行聚类,这会将同一页上的行放在一起。在我的 SATA 磁盘上,我有以下内容:

    SET work_mem TO '300MB';
    EXPLAIN (analyze,buffers) SELECT id FROM foo WHERE val = 55;
    
      Bitmap Heap Scan on foo  (...) (actual time=90.315..35091.665 rows=215022 loops=1)
        Heap Blocks: exact=140489
        Buffers: shared hit=20775 read=120306 written=24124
    
    SET maintenance_work_mem TO '1GB';
    CLUSTER foo USING val_index;
    EXPLAIN (analyze,buffers) SELECT id FROM foo WHERE val = 55;
    
      Bitmap Heap Scan on foo  (...) (actual time=49.215..407.505 rows=215022 loops=1)
        Heap Blocks: exact=1163
        Buffers: shared read=1755
    

    当然,这是一次性操作,随着时间的推移,它会一点一点地变长。

    【讨论】:

      【解决方案3】:

      您可以尝试降低 random_page_cost -- 对于 SSD,它可以是 1。其次,您可以增加一个 work_mem .. 10MB 对于当前具有千兆字节 RAM 的服务器来说是相对较低的值。您应该重新检查 Effective_cache_size - 它也可能太低。

      work_mem * max_connection * 2 + shared_buffers < RAM dedicated for Postgres
      effective_cache ~ shared_buffers + file system cache
      

      【讨论】:

      • 将 effective_cache_size 设置为 128mb 并将 work_mem 设置为 200mb 并没有帮助,random_page_cost 现在为 1。
      • 您可以尝试撰写索引。您可以尝试通过增加 cpu_tuple_cost 来惩罚行操作。您必须找到适合您系统的 work_mem、cpu_tuple_cost、random_page_cost、seq_page_cost 的组合。
      • 您建议使用什么撰写索引?我只按单个字段过滤,所以复合索引是无稽之谈,不是吗?
      猜你喜欢
      • 2012-08-28
      • 2019-04-19
      • 2017-04-06
      • 2014-12-06
      • 2020-04-23
      • 2017-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多