【问题标题】:Understanding Order by using top-N heapsort in postgres通过在 postgres 中使用 top-N 堆排序来理解 Order
【发布时间】:2015-11-26 10:39:47
【问题描述】:

在没有索引的理论场景中,带限制的 order by 必须扫描所有数据,然后应用 order by 然后只应用限制,因为我们只能在之后获得前 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 可视化,我无法理解这是如何工作的?任何人都可以理解这一点。我的假设是否正确?

【问题讨论】:

    标签: database performance postgresql


    【解决方案1】:

    我没有深入阅读源代码,但据我所知,它维护了一个大小有限的堆。

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

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

    如果它小于所有当前值或某些当前值,则将其插入堆中的适当位置,所有内容都向下移动 1,并将最后一个条目从堆中剔除。

    1618 (在 git master 中),case TSS_BOUNDED:puttuple_common 中查看src/backend/utils/sort/tuplesort.c

    【讨论】:

    • 这真的很有趣。一定要提高我的C技能。谢谢:)
    【解决方案2】:

    PostgreSQL 为此使用“top-N 排序堆排序”。在对这样的查询执行“EXPLAIN ANALYZE”时,您可以看到这一点。 没有索引就无法避免全表扫描。然而,前 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 参数

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 2022-07-11
        • 2016-10-23
        • 1970-01-01
        • 2017-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-17
        • 2013-11-27
        • 2012-02-14
        相关资源
        最近更新 更多