Postgresql查询优化不允许内部/外部联接

ImN*_*oob 6 database postgresql join query-optimization postgresql-performance

我得到了这个查询来优化POSTGRESQL 9.2:

SELECT C.name, COUNT(DISTINCT I.id) AS NumItems, COUNT(B.id)
FROM Categories C INNER JOIN Items I ON(C.id = I.category) 
                  INNER JOIN Bids B ON (I.id = B.item_id)
GROUP BY C.name
Run Code Online (Sandbox Code Playgroud)

作为我学校作业的一部分.

我在各自的表上创建了这些索引:items(category)- >第二个b +树,bids(item_id)- >第二个b +树,categories(id)- >主索引在这里,

奇怪的是,PostgreSQL正在对我的Items,Categories和Bids表进行顺序扫描,当我设置时enable_seqscan=off,索引搜索比下面的结果更可怕.

当我在PostgreSQL中运行解释时,这就是结果:请不要删除它们,因为它们是重要的!

GroupAggregate  (cost=119575.55..125576.11 rows=20 width=23) (actual time=6912.523..9459.431 rows=20 loops=1)
  Buffers: shared hit=30 read=12306, temp read=6600 written=6598
  ->  Sort  (cost=119575.55..121075.64 rows=600036 width=23) (actual time=6817.015..8031.285 rows=600036 loops=1)
        Sort Key: c.name
        Sort Method: external merge  Disk: 20160kB
        Buffers: shared hit=30 read=12306, temp read=6274 written=6272
        ->  Hash Join  (cost=9416.95..37376.03 rows=600036 width=23) (actual time=407.974..3322.253 rows=600036 loops=1)
              Hash Cond: (b.item_id = i.id)
              Buffers: shared hit=30 read=12306, temp read=994 written=992
              ->  Seq Scan on bids b  (cost=0.00..11001.36 rows=600036 width=8) (actual time=0.009..870.898 rows=600036 loops=1)
                    Buffers: shared hit=2 read=4999
              ->  Hash  (cost=8522.95..8522.95 rows=50000 width=19) (actual time=407.784..407.784 rows=50000 loops=1)
                    Buckets: 4096  Batches: 2  Memory Usage: 989kB
                    Buffers: shared hit=28 read=7307, temp written=111
                    ->  Hash Join  (cost=1.45..8522.95 rows=50000 width=19) (actual time=0.082..313.211 rows=50000 loops=1)
                          Hash Cond: (i.category = c.id)
                          Buffers: shared hit=28 read=7307
                          ->  Seq Scan on items i  (cost=0.00..7834.00 rows=50000 width=8) (actual time=0.004..144.554 rows=50000 loops=1)
                                Buffers: shared hit=27 read=7307
                          ->  Hash  (cost=1.20..1.20 rows=20 width=19) (actual time=0.062..0.062 rows=20 loops=1)
                                Buckets: 1024  Batches: 1  Memory Usage: 1kB
                                Buffers: shared hit=1
                                ->  Seq Scan on categories c  (cost=0.00..1.20 rows=20 width=19) (actual time=0.004..0.028 rows=20 loops=1)
                                      Buffers: shared hit=1
Total runtime: 9473.257 ms
Run Code Online (Sandbox Code Playgroud)

请参阅explain.depesz.com上的此计划.

我只是想知道为什么会发生这种情况,即为什么索引使得查询与顺序扫描相比非常慢.

编辑:我想我已经设法通过postgresql文档发现了一些东西.Postgresql决定对某些表格进行seq扫描,例如出价和项目,因为它预测它必须检索表格中的每一行(比较实际时间之前括号中的行数和实际时间部分中的行数) ).顺序扫描更好地检索所有行.那部分什么都做不了.

我已经创建了额外的索引categories(name),下面的结果就是我所拥有的.它以某种方式改进但现在散列连接被嵌套循环替换.任何线索为什么?

GroupAggregate  (cost=0.00..119552.02 rows=20 width=23) (actual time=617.330..7725.314 rows=20 loops=1)
  Buffers: shared hit=178582 read=37473 written=14, temp read=2435 written=436
  ->  Nested Loop  (cost=0.00..115051.55 rows=600036 width=23) (actual time=0.120..6186.496 rows=600036 loops=1)
        Buffers: shared hit=178582 read=37473 written=14, temp read=2109 written=110
        ->  Nested Loop  (cost=0.00..26891.55 rows=50000 width=19) (actual time=0.066..2827.955 rows=50000 loops=1)
              Join Filter: (c.id = i.category)
              Rows Removed by Join Filter: 950000
              Buffers: shared hit=2 read=7334 written=1, temp read=2109 written=110
              ->  Index Scan using categories_name_idx on categories c  (cost=0.00..12.55 rows=20 width=19) (actual time=0.039..0.146 rows=20 loops=1)
                    Buffers: shared hit=1 read=1
              ->  Materialize  (cost=0.00..8280.00 rows=50000 width=8) (actual time=0.014..76.908 rows=50000 loops=20)
                    Buffers: shared hit=1 read=7333 written=1, temp read=2109 written=110
                    ->  Seq Scan on items i  (cost=0.00..7834.00 rows=50000 width=8) (actual time=0.007..170.464 rows=50000 loops=1)
                          Buffers: shared hit=1 read=7333 written=1
        ->  Index Scan using bid_itemid_idx on bids b  (cost=0.00..1.60 rows=16 width=8) (actual time=0.016..0.036 rows=12 loops=50000)
              Index Cond: (item_id = i.id)
              Buffers: shared hit=178580 read=30139 written=13
Total runtime: 7726.392 ms
Run Code Online (Sandbox Code Playgroud)

如果它更好,请看看这里的计划.

我已经设法通过在类别(id)和上创建索引将其减少到114062.92 items(category).Postgresql使用这两个索引来获得114062.92的成本.但是,现在postgresql通过不使用索引与我玩游戏!为什么这么马车?

Cra*_*ger 1

感谢您在没有询问的情况下发布EXPLAIN输出,以及EXPLAIN (BUFFERS, ANALYZE).

查询性能问题的一个重要部分可能是外部排序计划节点,该节点正在使用临时文件进行磁盘上合并排序:

Sort Method: external merge Disk: 20160kB
Run Code Online (Sandbox Code Playgroud)

您可以通过设置在内存中进行这种排序:

SET work_mem = '50MB';
Run Code Online (Sandbox Code Playgroud)

在运行查询之前。此设置还可以针对每个用户、每个数据库或全局进行设置postgresql.conf

我不相信添加索引会有多大好处,因为查询当前是结构化的。它需要读取并连接所有三个表中的所有行,而哈希连接可能是最快的方法。

我怀疑还有其他方法来表达该查询,这些方法将使用完全不同且更有效的执行策略,但我对它们可能是什么有一个大脑衰退,并且不想花时间来组成虚拟表到处玩。更多work_mem应该显着改善现有的查询。