ไม่ตรงกันอย่างมากระหว่างขนาดดัชนีและจำนวนบัฟเฟอร์ในแผนการดำเนินการ


10

ปัญหา

เรามีคำถามเช่น

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

1
อาจไม่เกี่ยวข้อง แต่คุณต้องการเข้าร่วมarticleหรือไม่ ดูเหมือนว่าคอลัมน์ทั้งหมดที่เกี่ยวข้องมาจากreservationตารางและ (สมมติว่ามี FK) ผลลัพธ์ควรเหมือนกัน
ypercubeᵀᴹ

นั่นเป็นคำถามที่ดีมาก และคุณพูดถูกไม่จำเป็น - นี่เป็นคำถามที่ใช้ในการตรวจสอบโดยทีมอื่น ยังคงเป็นอย่างน้อยมองไปที่แผนแบบสอบถามทุกอย่างอื่นเป็นเพียงการตกแต่งที่ดัชนีสแกนที่น่ารังเกียจ :)
Dezso

1
ให้ฉันเพิ่มว่าการลบการเข้าร่วมไม่ได้สร้างความแตกต่างอย่างมาก - การสแกนดัชนีมากเกินไปยังคงอยู่ที่นั่น
dezso

การเข้าถึงตารางขนมปังปิ้ง? แม้ว่าฉันสงสัยว่าคอลัมน์ใด ๆ ที่คุณแสดงจะมีการปิ้ง หากคุณมีฐานข้อมูลที่ไม่ได้ใช้งานสำหรับการทดสอบคุณสามารถเรียกใช้pg_stat_reset()จากนั้นเรียกใช้คิวรีจากนั้นค้นหาpg_statio_user_tablesเพื่อดูว่าแอตทริบิวต์นั้นมีบล็อกอยู่ที่ใด
jjanes

คำตอบ:


4

ฉันคิดว่ากุญแจสำคัญในที่นี้คือการอัปเดตจำนวนมากและการขยายตัวของดัชนี

ดัชนีมีพอยน์เตอร์ไปยังแถวในตารางที่ไม่ 'สด' อีกต่อไป นี่คือแถวที่อัปเดตเวอร์ชันเก่า เวอร์ชันแถวเก่าจะถูกเก็บรักษาไว้ชั่วขณะหนึ่งเพื่อตอบสนองคิวรีด้วยสแนปชอตเก่าจากนั้นเก็บไว้อีกครู่หนึ่งเพราะไม่มีใครต้องการทำงานของการลบออกบ่อยกว่าที่จำเป็น

เมื่อทำการสแกนดัชนีจะต้องไปที่แถวเหล่านี้แล้วสังเกตเห็นว่าไม่ปรากฏให้เห็นอีกต่อไปดังนั้นให้ละเว้นพวกเขา explain (analyze,buffers)คำสั่งไม่ได้รายงานเกี่ยวกับกิจกรรมนี้อย่างชัดเจนยกเว้นผ่านการนับบัฟเฟอร์อ่าน / ตีในกระบวนการของการตรวจสอบแถวเหล่านี้

มีรหัส "microvacuum" สำหรับ btrees เช่นเมื่อการสแกนกลับไปที่ดัชนีอีกครั้งมันจะจำได้ว่าตัวชี้ที่ถูกไล่ล่านั้นไม่มีชีวิตอีกต่อไปและทำเครื่องหมายว่าตายในดัชนี ด้วยวิธีนี้เคียวรีที่คล้ายกันถัดไปที่เรียกใช้ไม่จำเป็นต้องไล่มันลงมาอีกครั้ง ดังนั้นหากคุณเรียกใช้การค้นหาเดียวกันซ้ำอีกครั้งคุณอาจเห็นการเข้าถึงบัฟเฟอร์ลดลงใกล้เคียงกับสิ่งที่คุณคาดการณ์ไว้

คุณยังสามารถVACUUMจัดตารางได้บ่อยขึ้นซึ่งจะช่วยทำความสะอาดสิ่งอันดับที่ตายออกจากโต๊ะตัวเองไม่ใช่แค่เพียงบางส่วนของดัชนี โดยทั่วไปแล้วตารางที่มีดัชนีบางส่วนที่พลิกกลับสูงมักจะได้รับประโยชน์จากสุญญากาศเชิงรุกมากกว่าระดับเริ่มต้น


โปรดดูการแก้ไขของฉัน - สำหรับฉันดูเหมือนว่าแคชไม่ใช่ microvacuuming
dezso

หมายเลขใหม่ของคุณนั้นแตกต่างจากตัวเลขเก่าของคุณมาก (เป็นสองเท่าโดยประมาณ) ดังนั้นจึงยากที่จะตีความความหมายโดยไม่เห็นตัวเลขใหม่สำหรับแถวและแถวจริงที่กรองสำหรับการสแกนดัชนี
jjanes

เพิ่มแผนทั้งหมดตามที่พวกเขามองในวันนี้ จำนวนบัฟเฟอร์ที่ได้รับผลกระทบเพิ่มขึ้นอย่างมากตั้งแต่วันศุกร์เช่นเดียวกับการนับแถว
dezso

คุณมีการทำธุรกรรมระยะยาวแขวนอยู่รอบ ๆ ? หากเป็นเช่นนั้นอาจเป็นไปได้ว่าการสแกนดัชนียังคงติดตามแถวที่ไม่สามารถมองเห็นได้ (ซึ่งเป็นสาเหตุของการเข้าชมบัฟเฟอร์พิเศษ) แต่ยังไม่สามารถ microvacuum พวกเขาออกไปเพราะพวกเขาอาจจะมองเห็นคนที่มีอายุมากกว่า ภาพรวม
jjanes

ฉันไม่มีเลย - ธุรกรรมทั่วไปใช้เวลาน้อยกว่าหนึ่งวินาที บางครั้งไม่กี่วินาที แต่ไม่นาน
dezso
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.