问题
我们有一个类似的查询
SELECT COUNT(1)
FROM article
JOIN reservation ON a_id = r_article_id
WHERE r_last_modified < now() - '8 weeks'::interval
AND r_group_id = 1
AND r_status = 'OPEN';
由于超时(通常在10分钟后)更多,因此我决定调查此问题。
该EXPLAIN (ANALYZE, BUFFERS)
输出如下所示:
Aggregate (cost=264775.48..264775.49 rows=1 width=0) (actual time=238960.290..238960.291 rows=1 loops=1)
Buffers: shared hit=200483 read=64361 dirtied=666 written=8, temp read=3631 written=3617
I/O Timings: read=169806.955 write=0.154
-> Hash Join (cost=52413.67..264647.65 rows=51130 width=0) (actual time=1845.483..238957.588 rows=21644 loops=1)
Hash Cond: (reservation.r_article_id = article.a_id)
Buffers: shared hit=200483 read=64361 dirtied=666 written=8, temp read=3631 written=3617
I/O Timings: read=169806.955 write=0.154
-> Index Scan using reservation_r_article_id_idx1 on reservation (cost=0.42..205458.72 rows=51130 width=4) (actual time=34.035..237000.197 rows=21644 loops=1)
Filter: ((r_group_id = 1) AND (r_status = 'OPEN') AND (r_last_modified < (now() - '56 days'::interval)))
Rows Removed by Filter: 151549
Buffers: shared hit=200193 read=48853 dirtied=450 written=8
I/O Timings: read=168614.105 write=0.154
-> Hash (cost=29662.22..29662.22 rows=1386722 width=4) (actual time=1749.392..1749.392 rows=1386814 loops=1)
Buckets: 32768 Batches: 8 Memory Usage: 6109kB
Buffers: shared hit=287 read=15508 dirtied=216, temp written=3551
I/O Timings: read=1192.850
-> Seq Scan on article (cost=0.00..29662.22 rows=1386722 width=4) (actual time=23.822..1439.310 rows=1386814 loops=1)
Buffers: shared hit=287 read=15508 dirtied=216
I/O Timings: read=1192.850
Total runtime: 238961.812 ms
瓶颈节点显然是索引扫描。因此,让我们看一下索引定义:
CREATE INDEX reservation_r_article_id_idx1
ON reservation USING btree (r_article_id)
WHERE (r_status <> ALL (ARRAY['FULFILLED', 'CLOSED', 'CANCELED']));
大小和行号
它的大小(通过\di+
或通过访问物理文件报告)为36 MB。由于预订通常只在较短的时间内用在上面未列出的所有状态上,因此发生了很多更新,因此索引相当膨胀(此处浪费了大约24 MB)-仍然,其大小相对较小。
该reservation
表的大小约为3.8 GB,包含约4000万行。尚未关闭的保留数约为170,000(确切的数目在上面的索引扫描节点中报告)。
现在令人惊讶的是:索引扫描报告报告正在获取大量缓冲区(即8 kb页):
Buffers: shared hit=200193 read=48853 dirtied=450 written=8
从缓存和磁盘(或OS缓存)读取的数字总计为1.9 GB!
最坏的情况
另一方面,在最坏的情况下,当每个元组位于表的不同页面上时,将占去访问(21644 + 151549)+ 4608页(从表中获取的总行数加上从物理表中获取的索引页数)尺寸)。这仍然只有不到18万,远低于观察到的近25万。
有趣(也许很重要)的是,磁盘读取速度约为2.2 MB / s,这很正常,我猜。
所以呢?
有谁知道这种差异可能来自何处?
注意:明确地说,我们在这里有什么要改进/更改的想法,但是我真的很想了解我得到的数字-这就是问题所在。
更新:检查缓存或微真空的效果
根据jjanes的回答,我检查了立即重新运行完全相同的查询时会发生什么。受影响的缓冲区的数量并没有真正改变。(为此,我将查询简化到最低限度,仍然显示了该问题。)这是我从第一次运行中看到的结果:
Aggregate (cost=240541.52..240541.53 rows=1 width=0) (actual time=97703.589..97703.590 rows=1 loops=1)
Buffers: shared hit=413981 read=46977 dirtied=56
I/O Timings: read=96807.444
-> Index Scan using reservation_r_article_id_idx1 on reservation (cost=0.42..240380.54 rows=64392 width=0) (actual time=13.757..97698.461 rows=19236 loops=1)
Filter: ((r_group_id = 1) AND (r_status = 'OPEN') AND (r_last_modified < (now() - '56 days'::interval)))
Rows Removed by Filter: 232481
Buffers: shared hit=413981 read=46977 dirtied=56
I/O Timings: read=96807.444
Total runtime: 97703.694 ms
在第二个之后:
Aggregate (cost=240543.26..240543.27 rows=1 width=0) (actual time=388.123..388.124 rows=1 loops=1)
Buffers: shared hit=460990
-> Index Scan using reservation_r_article_id_idx1 on reservation (cost=0.42..240382.28 rows=64392 width=0) (actual time=0.032..385.900 rows=19236 loops=1)
Filter: ((r_group_id = 1) AND (r_status = 'OPEN') AND (r_last_modified < (now() - '56 days'::interval)))
Rows Removed by Filter: 232584
Buffers: shared hit=460990
Total runtime: 388.187 ms
pg_stat_reset()
在该数据库上运行该数据库,然后运行查询,然后查看该数据库pg_statio_user_tables
在何处将这些块归为属性。
article
吗?似乎所有涉及的列都来自reservation
表,并且(假设)有一个FK,结果应该是相同的。