提高查询速度:大postgres表中的简单SELECT

时间:2012-11-05 15:07:05

标签: sql performance postgresql postgresql-performance

我在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 config的一些参数:

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

任何帮助,评论或想法都会非常感激。

提前谢谢。

4 个答案:

答案 0 :(得分:33)

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

PostgreSQL 9.2有一个名为index-only scans的功能,允许它(通常)在不访问表的情况下回答查询。您可以将此与自动维护顺序的btree索引属性结合使用,以快速进行此查询。你提到int1int2和两个花车:

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

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

答案 1 :(得分:20)

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

  1. 重新组织数据以减少对数据的搜索。
  2. 使用PostgreSQL 9.2功能“仅索引扫描”
  3. 由于我在PostgreSQL 9.1服务器下,我决定选择“1”。

    我制作了一份表格副本。所以现在我有两次相同数据的同一个表。我为每一个创建了一个索引,第一个索引由(int1)索引,第二个索引由(int2)索引。然后我将它们(CLUSTER表USING ind_intX)按其各自的索引聚类。

    我现在正在发布同一查询的EXPLAIN ANALYZE,在其中一个集群表中完成:

                                                             QUERY PLAN                                                            
    ---------------------------------------------------------------------------------------------------------------------------------------------  
    Index Scan using lec_sim_lec2id_ind on lec_sim_lec2id  (cost=0.00..21626.82 rows=6604 width=36) (actual time=0.051..1.500 rows=8119 loops=1)
    Index Cond: (lec2_id = 12300)  Total runtime:
    1.822 ms (3 rows)
    

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

    非常感谢willglynn。

答案 2 :(得分:3)

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

在父表和子表上执行手动VACUUM的原因有很大不同。即使父表正在完成其VACUUM流程,我也会从10分钟超时到一秒钟内的结果。

我对此的看法是,即使对于v9.1,常规的VACUUM操作仍然很重要。我这样做的原因是我注意到autovacuum没有在任何一个表上运行至少两周,从那以后发生了很多upserts和insert。可能我需要改进autovacuum触发器来解决这个问题,但是我可以说的是,如果清理了所有内容,那么具有几十亿行的640GB表确实表现良好。我还没有对桌子进行分区以获得良好的性能。

答案 3 :(得分:0)

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

random_page_cost=1.0

在你的postgresql.conf中。

默认值为random_page_cost=4.0,这针对具有较高寻道时间(如旧旋转磁盘)的存储进行了优化。这改变了寻求的成本计算,减少了你的记忆(最终可能会进行交换)

此设置单独将我的过滤查询从8秒减少到2秒,在长表上有几百万条记录。

另一项重大改进来自于在我的桌子上制作所有booleen列的索引。这将2秒查询减少到大约1秒。检查@ willglynn的答案。

希望这有帮助!