문제
우리는 같은 쿼리를
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+
실제 파일에서 보고 하거나 실제 파일을 방문하여 크기 는 36MB입니다. 예약은 일반적으로 위에 나열되지 않은 모든 상태에서 비교적 짧은 시간 만 소비하므로 업데이트가 많이 발생하므로 인덱스가 부풀어 오릅니다 (약 24MB가 낭비 됨). 그러나 여전히 크기는 비교적 작습니다.
reservation
표는 약 40 만 행을 포함, 크기가 3.8 GB에 관한 것입니다. 아직 닫히지 않은 예약 수는 약 170,000입니다 (정확한 수는 위의 인덱스 스캔 노드에보고 됨).
놀랍게도 : 인덱스 스캔은 대량의 버퍼 (8kb 페이지)를 가져 오는 것을보고합니다.
Buffers: shared hit=200193 read=48853 dirtied=450 written=8
캐시 및 디스크 (또는 OS 캐시)에서 읽은 숫자는 최대 1.9GB입니다.
최악의 시나리오
반면에 최악의 시나리오는 모든 튜플이 테이블의 다른 페이지에있을 때 방문 (21644 + 151549) + 4608 페이지 (테이블에서 가져온 총 행과 실제 페이지의 색인 페이지 번호)를 설명합니다. 크기). 이것은 여전히 180,000 미만으로 관찰 된 거의 250,000보다 훨씬 낮습니다.
흥미롭고 중요한 것은 디스크 읽기 속도가 약 2.2MB / 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가 있다고 가정하면 결과는 동일해야합니다.