30

我在 Postgres 数据库上的 SELECT 查询中遇到了速度问题。

我有一个表,其中有两个整数列作为键: (int1,int2) 这个表有大约 7000 万行。

我需要在这种环境中进行两种简单的 SELECT 查询:

SELECT * FROM table WHERE int1=X;
SELECT * FROM table WHERE int2=X;

这两个选择返回这 7000 万行中的大约 10.000 行。为了尽可能快地工作,我考虑使用两个 HASH 索引,每列一个。不幸的是,结果并不是那么好:

                                                               QUERY PLAN                                                               
----------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on lec_sim  (cost=232.21..25054.38 rows=6565 width=36) (actual time=14.759..23339.545 rows=7871 loops=1)
   Recheck Cond: (lec2_id = 11782)
   ->  Bitmap Index Scan on lec_sim_lec2_hash_ind  (cost=0.00..230.56 rows=6565 width=0) (actual time=13.495..13.495 rows=7871 loops=1)
         Index Cond: (lec2_id = 11782)
 Total runtime: 23342.534 ms
(5 rows)

这是这些查询之一的 EXPLAIN ANALYZE 示例。大约需要 23 秒。我的期望是在不到一秒钟的时间内获得这些信息。

这些是 postgres db 配置的一些参数:

work_mem = 128MB
shared_buffers = 2GB
maintenance_work_mem = 512MB
fsync = off
synchronous_commit = off
effective_cache_size = 4GB

任何帮助、评论或想法将不胜感激。

先感谢您。

4

4 回答 4

34

将我的评论提取到答案中:这里的索引查找非常快——所有时间都花在检索实际行上。23 秒 / 7871 行 = 每行 2.9 毫秒,这对于检索分散在磁盘子系统中的数据是合理的。搜索很慢;您可以 a) 将数据集放入 RAM,b) 购买 SSD,或 c) 提前组织数据以最大程度地减少搜索。

PostgreSQL 9.2 有一个称为仅索引扫描的功能,它允许它(通常)在不访问表的情况下回答查询。您可以将此与btree自动维护顺序的索引属性相结合,以使此查询快速。你提到int1, int2, 和两个花车:

CREATE INDEX sometable_int1_floats_key ON sometable (int1, float1, float2);
CREATE INDEX sometable_int2_floats_key ON sometable (int2, float1, float2);

SELECT float1,float2 FROM sometable WHERE int1=<value>; -- uses int1 index
SELECT float1,float2 FROM sometable WHERE int2=<value>; -- uses int2 index

另请注意,这不会神奇地擦除磁盘搜索,它只是将它们从查询时间移动到插入时间。由于您正在复制数据,因此它还会占用您的存储空间。不过,这可能是您想要的权衡。

于 2012-11-05T16:20:27.070 回答
21

谢谢你,威林。正如您所注意到的,问题在于通过 HD 进行搜索而不是查找索引。您提出了许多解决方案,例如将数据集加载到 RAM 中或购买 SSD HD。但是忘记这两个,涉及管理数据库本身之外的东西,你提出了两个想法:

  1. 重新组织数据以减少对数据的查找。
  2. 使用 PostgreSQL 9.2 功能“仅索引扫描”

由于我在 PostgreSQL 9.1 服务器下,我决定采用选项“1”。

我复制了一张桌子。所以现在我有两次相同的数据相同的表。我为每一个都创建了一个索引,第一个由 (int1) 索引,第二个由 (int2) 索引。然后我通过各自的索引将它们都聚集在一起(使用 ind_intX 的 CLUSTER 表)。

我现在发布相同查询的 EXPLAIN ANALYZE,在这些聚簇表之一中完成:

                                                         查询计划                                                            
-------------------------------------------------- -------------------------------------------------- -----------------------------------------  
在 lec_sim_lec2id 上使用 lec_sim_lec2id_ind 进行索引扫描(成本=0.00..21626.82 行=6604 宽度=36)(实际时间=0.051..1.500 行=8119 循环=1)
索引条件:(lec2_id = 12300)总运行时间:
1.822 毫秒(3 行)

现在寻找真的很快。我从 23 秒下降到 ~2 毫秒,这是一个令人印象深刻的改进。我认为这个问题对我来说已经解决了,我希望这对遇到同样问题的其他人也有用。

非常感谢威格林。

于 2012-11-06T13:26:58.243 回答
3

我有一个超慢查询的案例,其中在 3300 万行的表和 24 亿行的子表之间执行简单的一对多连接(在 PG v9.1 中)。我对子表的外键索引执行了 CLUSTER,但发现这并没有解决我的查询超时问题,即使是最简单的查询也是如此。运行 ANALYZE 也没有解决问题。

产生巨大差异的是在父表和子表上执行手动 VACUUM。即使父表正在完成其 VACUUM 过程,我也从 10 分钟超时到一秒钟内返回结果。

我从中得出的结论是,常规的 VACUUM 操作仍然很关键,即使对于 v9.1 也是如此。我这样做的原因是我注意到 autovacuum 至少两周没有在任何一个表上运行,并且从那时起发生了很多 upserts 和 inserts。可能我需要改进 autovacuum 触发器来解决这个问题,但我可以说的是,如果所有内容都被清理干净,一个有几十亿行的 640GB 表确实可以很好地执行。我还没有必要对表进行分区以获得良好的性能。

于 2014-02-21T16:53:08.907 回答
0

对于一个非常简单有效的单行,如果您的 postgres 机器上有快速固态存储,请尝试设置:

random_page_cost=1.0

在你的在你的postgresql.conf

默认值是random_page_cost=4.0,这针对具有高寻道时间的存储进行了优化,例如旧的旋转磁盘。这改变了寻找的成本计算并且更少地依赖你的内存(最终可能会被交换)

仅此设置就将我的过滤查询从 8 秒缩短到 2 秒,在具有几百万条记录的长表上。

另一个主要改进来自于使用我表上的所有布尔列创建索引。这将 2 秒的查询减少到大约 1 秒。检查@willglynn 的答案。

希望这可以帮助!

于 2018-03-27T17:13:31.147 回答