为什么以下Postgres SQL查询需要这么长时间?

时间:2011-05-30 09:37:59

标签: sql postgresql

原始查询如下

SELECT "TIME", "TRADEPRICE"
  FROM "YEAR" where "DATE"='2010-03-01' 
  and "SECURITY"='STW.AX' 
  AND "TIME" < '10:16:00' 
  AND "TYPE" = 'TRADE'
  ORDER BY "TIME" ASC LIMIT 3

我已经建立了三个指数如下

Columns "DATE" DESC NULLS LAST
Columns "SECURITY" DESC NULLS LAST
Columns "TIME" DESC NULLS LAST

我没有为TYPE编制索引,因为它只接受两个可能值中的一个

解释分析产生以下

"Limit  (cost=50291.28..50291.28 rows=3 width=16) (actual time=1794484.566..1794484.567 rows=3 loops=1)"
"  ->  Sort  (cost=50291.28..50291.29 rows=4 width=16) (actual time=1794484.562..1794484.563 rows=3 loops=1)"
"        Sort Key: "TIME""
"        Sort Method:  top-N heapsort  Memory: 25kB"
"        ->  Bitmap Heap Scan on "YEAR"  (cost=48569.54..50291.24 rows=4 width=16) (actual time=1794411.662..1794484.498 rows=20 loops=1)"
"              Recheck Cond: (("SECURITY" = 'STW.AX'::bpchar) AND ("DATE" = '2010-03-01'::date))"
"              Filter: (("TIME" < '10:16:00'::time without time zone) AND ("TYPE" = 'TRADE'::bpchar))"
"              ->  BitmapAnd  (cost=48569.54..48569.54 rows=430 width=0) (actual time=1794411.249..1794411.249 rows=0 loops=1)"
"                    ->  Bitmap Index Scan on security_desc  (cost=0.00..4722.94 rows=166029 width=0) (actual time=1793917.506..1793917.506 rows=1291933 loops=1)"
"                          Index Cond: ("SECURITY" = 'STW.AX'::bpchar)"
"                    ->  Bitmap Index Scan on date_desc  (cost=0.00..43846.35 rows=2368764 width=0) (actual time=378.698..378.698 rows=2317130 loops=1)"
"                          Index Cond: ("DATE" = '2010-03-01'::date)"
"Total runtime: 1794485.224 ms"

数据库在Core2Quad上运行大约10亿行,在Ubuntu 64bit上运行8gig RAM。当然这个查询不应该花半个小时

3 个答案:

答案 0 :(得分:12)

  

数据库在Core2Quad上运行大约10亿行,在Ubuntu 64bit上运行8gig RAM。当然这个查询不应该花半个小时

由于您设置索引的方式,这需要半个小时。

您的查询没有可用于直接转到所需行的多列索引。它做了下一个最好的事情,即对几乎没有选择性的索引进行位图索引扫描,并对结果集进行前3排序。

有问题的两个索引,关于安全性和日期,分别产生1.3M和2.3M行。将它们组合在一起将会非常缓慢,因为您会随机查找超过一百万行并过滤每一行。

添加侮辱伤害,您的数据结构是这样两个高度相关的字段(日期和时间)分别存储和操作。这会混淆查询计划程序,因为Postgres不会收集关联数据。因此,您的查询几乎总是依赖于过滤大量数据,并按照单独的标准对过滤集进行排序。

我建议进行以下更改:

  1. 更改表格并添加类型为timestamp with time zone的日期时间列。将日期和时间列合并到其中。

  2. 相应地删除日期和时间字段,以及它们上的索引。同时删除安全性索引。

  3. 在(security,datetime)上创建索引。 (除非您的订购标准也包含这些条款,否则不要先将空值置于最后/空值。)

  4. 根据您的选择,在(datetime)或on(datetime,security)上添加单独的索引,如果您需要执行在日期或日期范围内对所有交易执行统计信息的查询。

  5. 一旦完成上述操作,真空分析整个混乱。

  6. 然后你就可以像这样重写你的查询:

    SELECT "TIME", "TRADEPRICE"
      FROM "YEAR"
      WHERE '2010-03-01 00:00:00' <= "DATETIME" AND "DATETIME" < '2010-03-01 10:16:00'
      AND "SECURITY"='STW.AX' 
      AND "TYPE" = 'TRADE'
      ORDER BY "DATETIME" ASC LIMIT 3
    

    这将产生最优化的计划:从(安全性,日期时间)过滤索引扫描中检索前3行,我期望(因为你有十亿行),最多需要25毫秒。 / p>

答案 1 :(得分:1)

将许多搜索词的综合索引添加到一起,例如ON YEAR (TYPE, SECURITY, DATE, TIME)。然后,数据库可以在单个索引中查找以匹配所有索引,而不必搜索多个索引并将所有结果整理在一起(位图索引扫描)。

究竟哪些列(例如包括TYPE或不包括?)以及您在索引中包含它们的顺序取决于数据特征以及您正在执行的其他类型的查询(因为您可以重新使用任何复合索引的左子集(免费),所以试验一下;但为了鼓励订单优化,请将ORDER BY列保留为最后使用的索引列/方向。

您可能还希望ANALYZE更新查询计划程序的统计信息,因为某些行数猜测似乎有些偏差。

答案 2 :(得分:0)

  

我没有为TYPE编制索引,因为它只接受两个可能值中的一个

您必须了解索引的工作原理以及它们的工作原理。索引将索引数据复制到仅包含指定索引数据的精简小索引块。从您的X GB原始数据中只剩下X / 20(猜测)大小。如果指定使用未编制索引的数据的查询意味着对于满足其他查询条件的每个记录,则DBMS必须将相应的原始数据块读取到索引块以确定它是否与查询条件匹配。 / p>

最佳情况是至少有一个索引包含查询所声明的每个要求,因此不需要对数据块进行查找。

另一个提示:通常最好列出一些列,这些列的值通常最后作为范围(在您的情况下为“TIME”)查询。

我的建议:删除所有索引。使用TIME(ASC),DATE,SECURITY,TYPE(按此顺序)字段创建索引。使用查询

SELECT "TIME", "TRADEPRICE"
  FROM "YEAR"
  WHERE "TIME" < '10:16:00'
  AND "DATE"='2010-03-01' 
  AND "SECURITY"='STW.AX' 
  AND "TYPE" = 'TRADE'

观看令人难以置信的速度。