PostgreSQL 是否总是以相同的顺序依次扫描页面?

dcc*_*310 4 postgresql cache

另一个问题来自我发现EXPLAINPostgreSQL 中很棒的新选项。这一项侧重于BUFFERS选项。

这里是EXPLAIN

EXPLAIN (ANALYZE, BUFFERS) SELECT event_time FROM ui_events_v2  WHERE page ~ 'foo' LIMIT 1;
                                                     QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.00..1539.68 rows=1 width=8) (actual time=0.858..0.858 rows=1 loops=1)
   Buffers: shared read=10
   I/O Timings: read=0.735
   ->  Seq Scan on ui_events_v2  (cost=0.00..3313394.58 rows=2152 width=8) (actual time=0.857..0.857 rows=1 loops=1)
         Filter: (page ~ 'foo'::text)
         Rows Removed by Filter: 112
         Buffers: shared read=10
         I/O Timings: read=0.735
 Planning Time: 6.455 ms
 Execution Time: 0.877 ms
Run Code Online (Sandbox Code Playgroud)

它非常快 - 这个查询在冷启动时非常慢。这是一个 30M 的行表,有七行page包含 substring foo。与此查询匹配的第一行似乎显示 604k 页,这将是 中提到的 ~3M 页的 ~20% pg_class

SELECT min(ctid) FROM ui_events_v2 WHERE page ~ 'foo';
     min
-------------
 (604435,10)
Run Code Online (Sandbox Code Playgroud)

我的假设是,即使这个表的每一页都在 PostgreSQL 或操作系统缓存中,它仍然需要遍历一些线性页面列表以进行顺序扫描。该部分Buffers: shared read=100.877 ms执行时间向我表明它以某种方式“从停止的地方开始”并从远离开头的页面开始。我认为它通过页面 ID 移动并在移动时查看每个页面的缓存,但它是否可能从缓存本身开始?如果是这样,它如何发现非缓存页面?

我知道不能保证以任何特定顺序找到行,但我认为在相同的查询策略中,所遵循的路径将相对相似。

jja*_*nes 9

如果多个进程试图对同一个表进行 seq 扫描,它会尝试将它们排成一行,以便所有进程几乎同时读取相同的页面,以最大限度地提高缓存命中率。它通过让后来者从中间开始(现有的人碰巧所在的地方)来做到这一点,然后绕到末尾以在开始之前的页面完成表格。这样做的一个副作用是,如果一个 seq 扫描提前停止,下一个将在上一个停止的同一页面上启动。因此,如果整个 LIMIT 可以通过一页中的行来满足,那么像您这样的扫描只会一遍又一遍地扫描同一页。

您可以使用 关闭此优化set synchronize_seqscans=off,然后它们都从表中的第一个块开始。