ปัญหา
เรามีคำถามเช่น
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 ในขนาดที่มีประมาณ 40 ล้านแถว จำนวนการจองที่ยังไม่ปิดประมาณ 170,000 (จำนวนที่แน่นอนจะรายงานในโหนดสแกนดัชนีด้านบน)
ตอนนี้น่าประหลาดใจ: การสแกนดัชนีรายงานดึงบัฟเฟอร์จำนวนมาก (นั่นคือหน้าขนาด 8 kb):
Buffers: shared hit=200193 read=48853 dirtied=450 written=8
ตัวเลขที่อ่านจากแคชและดิสก์ (หรือแคชของระบบปฏิบัติการ) เพิ่มได้สูงสุด 1.9 GB!
สถานการณ์เลวร้ายที่สุด
ในอีกกรณีที่เลวร้ายที่สุดกรณีที่ทุก tuple ตั้งอยู่บนหน้าอื่นของตารางจะบัญชีสำหรับการเยี่ยมชม (21644 + 151549) + หน้า 4608 (รวมแถวเรียกจากตารางบวกจำนวนหน้าดัชนีจากทางกายภาพ ขนาด). นี่เป็นเพียงต่ำกว่า 180,000 - ต่ำกว่าที่สังเกตเกือบ 250,000
น่าสนใจ (และอาจสำคัญ) คือความเร็วในการอ่านดิสก์อยู่ที่ประมาณ 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) ผลลัพธ์ควรเหมือนกัน