在postgres中使用top-N heapsort了解Order

时间:2015-11-26 10:39:47

标签: database performance postgresql

在缺少索引的理论场景中,带限制的订单必须扫描所有数据,然后应用订单,然后仅应用限制,因为我们只能在获得前10行(例如)之后排序

但是postgres在这里并不聪明,下面的计划就是这个故事。

按限制排序

learning=# explain (analyze,buffers) select * from temp order by userid limit 10;
                                                          QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=81745.51..81745.54 rows=10 width=41) (actual time=2064.275..2064.278 rows=10 loops=1)
   Buffers: shared hit=13 read=18644
   ->  Sort  (cost=81745.51..86735.41 rows=1995958 width=41) (actual time=2064.273..2064.274 rows=10 loops=1)
         Sort Key: userid
         Sort Method: top-N heapsort  Memory: 25kB
         Buffers: shared hit=13 read=18644
         ->  Seq Scan on temp  (cost=0.00..38613.58 rows=1995958 width=41) (actual time=35.053..1652.660 rows=1995958 loops=1)
               Buffers: shared hit=10 read=18644
 Planning time: 0.167 ms
 Execution time: 2064.335 ms
(10 rows)

无限制地订购

learning=# explain (analyze,buffers) select * from temp order by userid;
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Sort  (cost=308877.61..313867.51 rows=1995958 width=41) (actual time=2685.680..3293.698 rows=1995958 loops=1)
   Sort Key: userid
   Sort Method: external merge  Disk: 99504kB
   Buffers: shared hit=42 read=18612, temp read=12440 written=12440
   ->  Seq Scan on temp  (cost=0.00..38613.58 rows=1995958 width=41) (actual time=0.069..286.556 rows=1995958 loops=1)
         Buffers: shared hit=42 read=18612
 Planning time: 0.066 ms
 Execution time: 3540.545 ms
(8 rows)

我的假设是postgres使用一种称为堆排序的算法(众所周知),并在获得前N行(限制)行时停止。

this可视化,我无法理解这是如何工作的?任何人都能理解这一点。我的上述假设是正确的吗?

3 个答案:

答案 0 :(得分:2)

我还没有深入阅读来源,但据我所知,它保持了一个有限大小的堆。

按顺序使用输入值。它将堆填充到目标数量的元组后,它开始检查每个新值,看它是否大于所有当前值,小于所有当前值,或者是否适合堆内。

如果它大于所有当前值(假设ASC排序),它将被丢弃,因为我们已经有足够低的值。

如果它小于所有当前值或某些当前值,它会在堆中的适当位置插入,所有内容都会向下移动一个,并且它会使堆中的最后一个条目崩溃

src/backend/utils/sort/tuplesort.c的{​​{1}}周围1618(git master中)case TSS_BOUNDED:附近puttuple_common

答案 1 :(得分:0)

您链接到的可视化只是一个堆栈执行。堆数据结构是(二进制)树,其属性是每个节点中的值比其子节点中的值更大(或更小)。堆可以存储在数组中,这是可视化显示的内容。

堆可以用作有效的优先级队列。因为它只是部分排序,所以使用优先级队列比排序列表或类似的更便宜。这可能是Postgres所做的:如果从表中选择N个最大值,Postgres会保留到目前为止看到的N个最大值的优先级队列(由堆实现),其中最小的值是第一个(即在顶部堆)。如果新值甚至小于优先级队列的第一项,则可以丢弃新值。如果它更大,则从优先级队列中删除第一个值并插入新值。

答案 2 :(得分:0)

PostgreSQL为此使用“ top-N排序堆排序”。在像这样的查询上执行“ EXPLAIN ANALYZE”时,您可以看到该效果。 没有索引,就无法避免全表扫描。但是,top-N堆排序避免为所有行分配内存,因为 只关心前10个。

例如,我周围有一个600万的条目表,并要求一个未索引的列排在前10行。在应用LIMIT 10的情况下,它告诉我:

      Sort Method: top-N heapsort  Memory: 25kB
      Without:
      Sort Method: quicksort  Memory: (some value)kB

因此,如果Postgres必须存储所有排序的600万行,则将需要那么多MB的工作内存。如果(请参阅SHOW work_mem)它低于该值 这将导致它向磁盘写入大量临时文件:

      Sort Method: external merge  Disk: 99504kB

要使用快速内存​​扫描,您必须增加work_mem参数