【问题标题】:Bitmap Heap Scan performance位图堆扫描性能
【发布时间】:2013-05-06 15:54:19
【问题描述】:

我有一个大报表表。位图堆扫描步骤耗时 5 秒以上。

有什么我可以做的吗?我向表中添加列,重新索引它使用的索引会有帮助吗?

我对数据进行联合和求和,因此我不会将 500K 记录返回给客户端。
我使用 postgres 9.1。
这里解释一下:

 Bitmap Heap Scan on foo_table  (cost=24747.45..1339408.81 rows=473986 width=116) (actual time=422.210..5918.037 rows=495747 loops=1)
   Recheck Cond: ((foo_id = 72) AND (date >= '2013-04-04 00:00:00'::timestamp without time zone) AND (date <= '2013-05-05 00:00:00'::timestamp without time zone))
   Filter: ((foo)::text = 'foooooo'::text)
   ->  Bitmap Index Scan on foo_table_idx  (cost=0.00..24628.96 rows=573023 width=0) (actual time=341.269..341.269 rows=723918 loops=1)

查询:

explain analyze
SELECT CAST(date as date) AS date, foo_id, ....
from foo_table
where foo_id = 72
and date >= '2013-04-04'
and date <= '2013-05-05'
and foo = 'foooooo'

Index def:
Index "public.foo_table_idx"
   Column    |            Type
-------------+-----------------------------
 foo_id      | bigint
 date        | timestamp without time zone

 btree, for table "public.external_channel_report"

表:
footext 字段,有 4 个不同的值。
foo_idbigint,目前有 10K 个不同的值。

【问题讨论】:

  • 请发布查询
  • 请添加表定义,包括索引。
  • 我仍然没有在您的“定义”中看到列foo 的定义。请添加 real 定义。另外:foo 的基数是多少? (它在功能上是否依赖于 foo_id?)
  • foo 是具有 4 个不同可能值的文本字段。 foo_id 是具有少于 10K 不同值的 bigint
  • 可惜你没有使用 9.2,你可以获得更多关于 I/O 和缓冲区访问的数据。

标签: sql postgresql indexing postgresql-performance


【解决方案1】:

(foo_id, foo, date) 上创建一个复合索引(按此顺序)。

请注意,如果您选择 500k 条记录(并将它们全部返回给客户端),这可能需要很长时间。

您确定需要客户端上的所有 50 万条记录(而不是某种聚合或 LIMIT)吗?

【讨论】:

  • 我在 resilt 上有一个聚合。 foo 是一个 enum ,所以它有 4 个可能的值。我应该如何确定索引的顺序?我是否需要与索引顺序相同的 where 列?你能解释一下为什么这是一个索引问题吗?过滤器索引返回几乎相同数量的行,如位图堆扫描?谢谢!!!!
【解决方案2】:

回复评论

我是否需要与索引顺序相同的 where 列?

WHERE 子句中的表达式顺序完全无关,SQL 不是过程语言。

修正错误

时间戳列应该命名为“日期”,原因有几个。显然,它是timestamp,而不是date。但更重要的是,date 在所有 SQL 标准中都是 reserved word,在 Postgres 中是类型和函数名称,不应用作标识符。

您应该为您的问题提供适当的信息,包括一个完整的表定义和关于现有索引的结论性信息。最好先阅读chapter about indexes in the manual

时间戳上的WHERE 条件很可能不正确:

and date >= '2013-04-04'
and date <= '2013-05-05'

时间戳列的上边框应该排除

and date >= '2013-04-04'
and date <  '2013-05-05'

索引

使用多列索引@Quassnoi provided,您的查询会更快,因为所有符合条件的行都可以从索引的一个连续数据块中读取。没有任何行是徒劳的(后来被取消资格),就像你现在拥有的那样。
但是 500k 行仍然需要一些时间。通常,您必须验证可见性并从表中获取其他列。 index-only scan 可能是 Postgres 9.2+ 中的一个选项。

这样的列顺序是最好的,因为经验法则是:列首先是相等的,然后是范围。更多解释和链接在this related answer on dba.SE

CLUSTER / pg_repack

您可以通过根据该索引精简表格来进一步加快速度,因此必须从表格中读取最少的块 - 如果您没有其他要求反对它!

如果您希望它更快,您可以简化表中行的物理顺序。如果您有能力将您的表专门锁定几秒钟(例如在下班时间)以重写您的表并根据索引对行进行排序:

ALTER TABLE foo_table CLUSTER ON idx_myindex_idx;

如果并发使用是个问题,考虑pg_repack,它可以在没有排他锁的情况下做同样的事情。

效果:需要从表中读取的块更少,并且所有内容都是预先排序的。如果您在桌子上写字,这是一种一次性效果,会随着时间的推移而恶化。所以你会时不时地重新运行它。

我从this related answer on dba.SE复制并改编了最后一章。

【讨论】:

  • 哇,谢谢你的回答。你在最后写了关于集群 - 所以你会不时重新运行它。 - 你的意思是集群索引,因为如果你这样做了,你需要再次运行它(我猜你的意思是 pg_repack)?
  • @AvihaiMarchiano:这句话涉及CLUSTER pg_repack。您似乎误判了CLUSTER 的性质。这是一次手术。随后的写操作将忽略它。仅保存用于下次运行 CLUSTER 的索引。按照我提供的手册的链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-27
  • 2012-04-26
  • 2017-06-23
  • 1970-01-01
  • 2011-10-11
  • 2011-09-29
相关资源
最近更新 更多