如何在PostgreSQL中优化查询数据?

4
我有一个查询在某些行上很慢。Postgres选择对一些行执行Seq扫描,而不是使用索引扫描,我认为这是因为实际上使用Seq扫描更快。
以下是在正常工作负载下使用索引的查询计划:http://explain.depesz.com/s/1A2o
EXPLAIN (ANALYZE, BUFFERS) SELECT "blocks".* FROM "blocks" INNER JOIN "jobs" ON "blocks"."job_id" = "jobs"."id" WHERE "jobs"."project_id" = 1;
                                                                 QUERY PLAN                                                                 
--------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.71..166.27 rows=19 width=130) (actual time=0.092..4.247 rows=2421 loops=1)
   Buffers: shared hit=350
   ->  Index Scan using index_jobs_on_project_id on jobs  (cost=0.29..18.81 rows=4 width=4) (actual time=0.044..0.099 rows=15 loops=1)
         Index Cond: (project_id = 1)
         Buffers: shared hit=17
   ->  Index Scan using index_blocks_on_job_id on blocks  (cost=0.42..36.67 rows=19 width=130) (actual time=0.021..0.133 rows=161 loops=15)
         Index Cond: (job_id = jobs.id)
         Buffers: shared hit=333
 Total runtime: 4.737 ms
(9 rows)

以下是选择顺序扫描来处理一种不太常见的工作负载的查询计划:http://explain.depesz.com/s/cJOd

EXPLAIN (ANALYZE, BUFFERS) SELECT "blocks".* FROM "blocks" INNER JOIN "jobs" ON "blocks"."job_id" = "jobs"."id" WHERE "jobs"."project_id" = 2;
                                                                 QUERY PLAN                                                                     
----------------------------------------------------------------------------------------------------------------------------------------------------
Hash Join  (cost=1138.64..11236.94 rows=10421 width=130) (actual time=5.212..72.604 rows=2516 loops=1)
 Hash Cond: (blocks.job_id = jobs.id)
 Buffers: shared hit=5671
 ->  Seq Scan on blocks  (cost=0.00..8478.06 rows=303206 width=130) (actual time=0.008..24.573 rows=298084 loops=1)
       Buffers: shared hit=5446
 ->  Hash  (cost=1111.79..1111.79 rows=2148 width=4) (actual time=3.346..3.346 rows=2164 loops=1)
       Buckets: 1024  Batches: 1  Memory Usage: 77kB
       Buffers: shared hit=225
       ->  Bitmap Heap Scan on jobs  (cost=40.94..1111.79 rows=2148 width=4) (actual time=0.595..2.158 rows=2164 loops=1)
             Recheck Cond: (project_id = 2)
             Buffers: shared hit=225
             ->  Bitmap Index Scan on index_jobs_on_project_id  (cost=0.00..40.40 rows=2148 width=0) (actual time=0.516..0.516 rows=2164 loops=1)
                   Index Cond: (project_id = 2)
                   Buffers: shared hit=8
 Total runtime: 72.767 ms
(15 rows)

在第一种情况下,该项目有15个作业和2421个块。在第二种情况下,该项目有2164个作业和2516个块。
有没有一种方法可以查询这些数据,以便第二个工作负载不会太慢?还是我正在处理某种最坏情况的工作负载?
编辑:
将random_page_cost更新为1.1并重新运行缓慢查询的EXPLAIN后:http://explain.depesz.com/s/xKdd
EXPLAIN (ANALYZE, BUFFERS) SELECT "blocks".* FROM "blocks" INNER JOIN "jobs" ON "blocks"."job_id" = "jobs"."id" WHERE "jobs"."project_id" = 2;

                                                              QUERY PLAN                                                                  
----------------------------------------------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.71..7634.08 rows=10421 width=130) (actual time=0.025..10.597 rows=2516 loops=1)
   Buffers: shared hit=9206
   ->  Index Scan using index_jobs_on_project_id on jobs  (cost=0.29..1048.99 rows=2148 width=4) (actual time=0.015..1.239 rows=2164 loops=1)
         Index Cond: (project_id = 32357)
         Buffers: shared hit=225
   ->  Index Scan using index_blocks_on_job_id on blocks  (cost=0.42..2.88 rows=19 width=130) (actual time=0.003..0.003 rows=1 loops=2164)
         Index Cond: (job_id = jobs.id)
         Buffers: shared hit=8981
 Total runtime: 10.925 ms
(9 rows)

非常好!看起来我需要花一些时间调整服务器配置。

你有哪些索引? - Dwayne Towell
jobs.project_idblocks.job_id 列上有索引。 - nfm
请显示完整的EXPLAIN(ANALYZE,BUFFERS)...而不仅仅是EXPLAIN - Craig Ringer
jobs.id呢? - Dwayne Towell
由于两个索引扫描的嵌套循环比位图索引扫描上的哈希连接快得多,因此我认为您的 random_page_cost 并没有准确反映出实际性能,至少在 RAM 或 shared_buffers 中缓存数据时如此。尝试设置 SET random_page_cost = 1.1 然后在该会话中重新运行。您可能还想将更多的 work_mem 分配给这个问题。 - Craig Ringer
1个回答

11
作为两个索引扫描的嵌套循环比基于位图索引的哈希联接快得多,我会说你的 random_page_cost 在缓存在 RAM 或 shared_buffers 中时并不能准确反映你的实际性能。

尝试将 SET random_page_cost = 1.1 并在该会话中重新运行。您也可以在问题上投入更多的 work_mem。

如果 random_page_cost 调整有效,则可能需要更新 postgresql.conf 来反映它。请注意,1.1 是一个相当极端的设置;默认值为4,seq_page_cost 是1,因此在配置文件中,我建议从2或1.5开始,以避免使其他计划变糟糕。


3
调整 random_page_cost 对我的一些查询产生了巨大的积极影响。这是一篇很好的文章,详细介绍了由于 random_page_cost 的性能改进:如何通过单个 PostgreSQL 配置更改将缓慢查询性能提高 50 倍 - Pete
对于非常快的存储后端(例如Intel Optane类或具有非常低延迟的硬件,用于QD1随机读取),我建议尝试将seq_page_costrandom_page_cost都设置为远低于1.0的值。如果您的IO速度比CPU性能快得多,则这是有意义的。例如,如果您有大量RAM,请尝试seq_page_cost=0.1random_page_cost=0.11 - Mikko Rantalainen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接