另一个问题来自我发现EXPLAIN
PostgreSQL 中很棒的新选项。这一项侧重于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=10
和0.877 ms
执行时间向我表明它以某种方式“从停止的地方开始”并从远离开头的页面开始。我认为它通过页面 ID 移动并在移动时查看每个页面的缓存,但它是否可能从缓存本身开始?如果是这样,它如何发现非缓存页面?
我知道不能保证以任何特定顺序找到行,但我认为在相同的查询策略中,所遵循的路径将相对相似。
如果多个进程试图对同一个表进行 seq 扫描,它会尝试将它们排成一行,以便所有进程几乎同时读取相同的页面,以最大限度地提高缓存命中率。它通过让后来者从中间开始(现有的人碰巧所在的地方)来做到这一点,然后绕到末尾以在开始之前的页面完成表格。这样做的一个副作用是,如果一个 seq 扫描提前停止,下一个将在上一个停止的同一页面上启动。因此,如果整个 LIMIT 可以通过一页中的行来满足,那么像您这样的扫描只会一遍又一遍地扫描同一页。
您可以使用 关闭此优化set synchronize_seqscans=off
,然后它们都从表中的第一个块开始。
归档时间: |
|
查看次数: |
308 次 |
最近记录: |