Postgres中的位图堆扫描非常慢

时间:2010-10-24 16:15:57

标签: performance postgresql indexing

我有以下包含流量测量数据的简单表:

CREATE TABLE "TrafficData"
(
  "RoadID" character varying NOT NULL,
  "DateID" numeric NOT NULL,
  "ExactDateTime" timestamp NOT NULL,
  "CarsSpeed" numeric NOT NULL,
  "CarsCount" numeric NOT NULL
)
CREATE INDEX "RoadDate_Idx" ON "TrafficData" USING btree ("RoadID", "DateID");

RoadID列唯一标识正在记录其数据的道路,而DateID标识数据的一年中的日期(1..365) - 基本上是ExactDateTime的四舍五入表示。

我有大约100.000.000行; “RoadID”列中有1.000个不同的值,“DateID”列中有365个不同的值。

然后我运行以下查询:

SELECT * FROM "TrafficData"
WHERE "RoadID"='Station_1'
AND "DateID">20100610 AND "DateID"<20100618;

这需要三个令人难以置信的秒才能完成,而我无法为我的生活找到原因。

EXPLAIN ANALYZE给出了以下输出:

Bitmap Heap Scan on "TrafficData"  (cost=104.84..9743.06 rows=2496 width=47) (actual time=35.112..2162.404 rows=2016 loops=1)
  Recheck Cond: ((("RoadID")::text = 'Station_1'::text) AND ("DateID" > 20100610::numeric) AND ("DateID" < 20100618::numeric))
  ->  Bitmap Index Scan on "RoadDate_Idx"  (cost=0.00..104.22 rows=2496 width=0) (actual time=1.637..1.637 rows=2016 loops=1)
        Index Cond: ((("RoadID")::text = 'Station_1'::text) AND ("DateID" > 20100610::numeric) AND ("DateID" < 20100618::numeric))
Total runtime: 2163.985 ms

我的规格:

  • Windows 7
  • Postgres 9.0
  • 4GB RAM

我非常感谢任何有用的指示!

3 个答案:

答案 0 :(得分:4)

缓慢的部分是从表中获取数据,因为索引访问速度似乎非常快。您可以优化RAM使用参数(请参阅http://wiki.postgresql.org/wiki/Performance_Optimizationhttp://www.varlena.com/GeneralBits/Tidbits/perf.html),或者通过发出CLUSTER命令来优化表中数据的布局(请参阅http://www.postgresql.org/docs/8.3/static/sql-cluster.html)。

CLUSTER "TrafficData" USING "RoadDate_Idx";

应该这样做。

答案 1 :(得分:2)

添加到Daniel的答案中,群集操作是一个关闭过程,重新安排磁盘上的数据。目的是从较少的磁盘块中获取2000个结果行。

由于这是虚拟数据,用于了解如何快速查询它,我建议重新加载它,其模式更接近于生成时的加载方式。我想这些数据一次只能生成一天,这将有效地导致DateID与磁盘上的位置之间的强相关性。如果是这种情况,那么我要按DateID进行聚类,或者将测试数据拆分为365个单独的加载,然后重新加载。

如果没有它,并且随机生成数据,您很可能不得不执行超过2000次的磁盘头搜索。

我还会检查您在Windows 7上运行的任何其他内容是否为这些您不需要的读取添加时间,例如确保读取的块不包含病毒签名,或同时执行自动调度的磁盘碎片整理(导致磁盘头几乎不会接近上次读取数据库块的位置)。

答案 2 :(得分:0)

  • 4GB RAM - &gt; 6+你有100M记录,这不是很大,但对于桌面机器内存可能很重要。如果这不是桌面,我不确定为什么你会有这么少的内存
  • AND "DateID">20100610 AND "DateID"<20100618; - &gt; DateID BETWEEN 20100611 AND 20100617;
  • 在DateID
  • 上创建索引
  • 删除字段名称旁边的所有双引号
  • 而不是VarChar,将RoadID设为文本字段