การแคชดัชนี PostgreSQL


16

ฉันมีปัญหาในการค้นหาคำอธิบาย 'lay' ของวิธีการจัดทำดัชนีแคชใน PostgreSQL ดังนั้นฉันต้องการตรวจสอบความเป็นจริงของสมมติฐานเหล่านี้ทั้งหมดหรือทั้งหมด:

  1. ดัชนี PostgreSQL เช่นแถวอยู่บนดิสก์ แต่อาจถูกแคช
  2. ดัชนีอาจอยู่ในแคชทั้งหมดหรือไม่ทั้งหมด
  3. ไม่ว่าจะเป็นแคชหรือไม่ขึ้นอยู่กับความถี่ในการใช้งาน (ตามที่กำหนดโดยตัววางแผนคิวรี)
  4. ด้วยเหตุนี้ดัชนี 'สมเหตุสมผล' ส่วนใหญ่จึงจะอยู่ในแคชตลอดเวลา
  5. ดัชนีอยู่ในแคชเดียวกัน ( buffer cache?) เป็นแถวดังนั้นพื้นที่แคชที่ใช้โดยดัชนีจะไม่สามารถใช้ได้กับแถว


แรงจูงใจของฉันสำหรับการทำความเข้าใจนี้ตามมาจากคำถามอื่นที่ฉันถามว่ามีข้อเสนอแนะว่าสามารถใช้ดัชนีบางส่วนในตารางซึ่งข้อมูลส่วนใหญ่จะไม่สามารถเข้าถึงได้

ก่อนดำเนินการนี้ฉันต้องการให้ชัดเจนว่าการใช้ดัชนีบางส่วนทำให้ได้เปรียบสองประการ:

  1. เราลดขนาดของดัชนีในแคชเพิ่มพื้นที่ว่างสำหรับแถวในแคช
  2. เราลดขนาดของ B-Tree ส่งผลให้เกิดการตอบแบบสอบถามที่รวดเร็วขึ้น

4
การใช้ดัชนีบางส่วนไม่เพียงมีประโยชน์เมื่อส่วนใหญ่ของข้อมูลจะเข้าถึงได้ยาก แต่เมื่อค่าบางอย่างเป็นเรื่องธรรมดา เมื่อค่าเป็นเรื่องธรรมดามากนักวางแผนจะใช้การสแกนตารางต่อไปแทนที่จะเป็นดัชนีดังนั้นการรวมค่าในดัชนีนั้นไม่มีจุดประสงค์
Eelke

คำตอบ:


19

เล่นกับpg_buffercache นิดหน่อยฉันสามารถหาคำตอบสำหรับคำถามของคุณได้บ้าง

  1. ค่อนข้างชัดเจน แต่ผลลัพธ์สำหรับ(5)ยังแสดงว่าคำตอบคือใช่
  2. ฉันยังไม่ได้ตั้งค่าตัวอย่างที่ดีสำหรับสิ่งนี้ตอนนี้มันเป็นใช่มากกว่าไม่ :) (ดูการแก้ไขของฉันด้านล่างคำตอบคือไม่ .)
  3. เนื่องจากผู้วางแผนเป็นผู้ตัดสินใจว่าจะใช้ดัชนีหรือไม่เราสามารถพูดว่าใช่ได้มันจะตัดสินใจแคช (แต่นี่ซับซ้อนกว่า)
  4. รายละเอียดที่แน่นอนของการแคชอาจมาจากซอร์สโค้ดฉันไม่พบหัวข้อนี้มากเกินไปยกเว้นอันนี้ (ดูคำตอบของผู้เขียน) อย่างไรก็ตามฉันค่อนข้างแน่ใจว่าสิ่งนี้จะซับซ้อนกว่าใช่หรือไม่ (อีกครั้งจากการแก้ไขของฉันคุณจะได้รับความคิดบางอย่าง - ตั้งแต่ขนาดแคชจะถูก จำกัด ผู้จัดทำดัชนี 'เหมาะสม' ในการแข่งขันสำหรับพื้นที่ที่มีอยู่ถ้าพวกเขามีมากเกินไปพวกเขาจะเตะแต่ละอื่น ๆ จากแคช -. ดังนั้นคำตอบคือค่อนข้างNO )
  5. ในฐานะที่เป็นแบบสอบถามง่ายๆที่มีการpg_buffercacheแสดงคำตอบคือใช่แน่นอน ควรทราบว่าข้อมูลตารางชั่วคราวจะไม่ถูกแคชที่นี่

แก้ไข

ฉันได้พบบทความที่ยอดเยี่ยมของ Jeremiah Peschka เกี่ยวกับการจัดเก็บตารางและดัชนี ด้วยข้อมูลจากที่นั่นฉันสามารถตอบ(2)เช่นกัน ฉันตั้งค่าการทดสอบขนาดเล็กเพื่อให้คุณสามารถตรวจสอบสิ่งเหล่านี้ด้วยตัวเอง

-- we will need two extensions
CREATE EXTENSION pg_buffercache;
CREATE EXTENSION pageinspect;


-- a very simple test table
CREATE TABLE index_cache_test (
      id serial
    , blah text
);


-- I am a bit megalomaniac here, but I will use this for other purposes as well
INSERT INTO index_cache_test
SELECT i, i::text || 'a'
FROM generate_series(1, 1000000) a(i);


-- let's create the index to be cached
CREATE INDEX idx_cache_test ON index_cache_test (id);


-- now we can have a look at what is cached
SELECT c.relname,count(*) AS buffers
FROM 
    pg_class c 
    INNER JOIN pg_buffercache b ON b.relfilenode = c.relfilenode 
    INNER JOIN pg_database d ON (b.reldatabase = d.oid AND d.datname = current_database())
GROUP BY c.relname
ORDER BY 2 DESC LIMIT 10;

             relname              | buffers
----------------------------------+---------
 index_cache_test                 |    2747
 pg_statistic_relid_att_inh_index |       4
 pg_operator_oprname_l_r_n_index  |       4
... (others are all pg_something, which are not interesting now)

-- this shows that the whole table is cached and our index is not in use yet

-- now we can check which row is where in our index
-- in the ctid column, the first number shows the page, so 
-- all rows starting with the same number are stored in the same page
SELECT * FROM bt_page_items('idx_cache_test', 1);

 itemoffset |  ctid   | itemlen | nulls | vars |          data
------------+---------+---------+-------+------+-------------------------
          1 | (1,164) |      16 | f     | f    | 6f 01 00 00 00 00 00 00
          2 | (0,1)   |      16 | f     | f    | 01 00 00 00 00 00 00 00
          3 | (0,2)   |      16 | f     | f    | 02 00 00 00 00 00 00 00
          4 | (0,3)   |      16 | f     | f    | 03 00 00 00 00 00 00 00
          5 | (0,4)   |      16 | f     | f    | 04 00 00 00 00 00 00 00
          6 | (0,5)   |      16 | f     | f    | 05 00 00 00 00 00 00 00
...
         64 | (0,63)  |      16 | f     | f    | 3f 00 00 00 00 00 00 00
         65 | (0,64)  |      16 | f     | f    | 40 00 00 00 00 00 00 00

-- with the information obtained, we can write a query which is supposed to
-- touch only a single page of the index
EXPLAIN (ANALYZE, BUFFERS) 
    SELECT id 
    FROM index_cache_test 
    WHERE id BETWEEN 10 AND 20 ORDER BY id
;

 Index Scan using idx_test_cache on index_cache_test  (cost=0.00..8.54 rows=9 width=4) (actual time=0.031..0.042 rows=11 loops=1)
   Index Cond: ((id >= 10) AND (id <= 20))
   Buffers: shared hit=4
 Total runtime: 0.094 ms
(4 rows)

-- let's have a look at the cache again (the query remains the same as above)
             relname              | buffers
----------------------------------+---------
 index_cache_test                 |    2747
 idx_test_cache                   |       4
...

-- and compare it to a bigger index scan:
EXPLAIN (ANALYZE, BUFFERS) 
SELECT id 
    FROM index_cache_test 
    WHERE id <= 20000 ORDER BY id
;


 Index Scan using idx_test_cache on index_cache_test  (cost=0.00..666.43 rows=19490 width=4) (actual time=0.072..19.921 rows=20000 loops=1)
   Index Cond: (id <= 20000)
   Buffers: shared hit=4 read=162
 Total runtime: 24.967 ms
(4 rows)

-- this already shows that something was in the cache and further pages were read from disk
-- but to be sure, a final glance at cache contents:

             relname              | buffers
----------------------------------+---------
 index_cache_test                 |    2691
 idx_test_cache                   |      58

-- note that some of the table pages are disappeared
-- but, more importantly, a bigger part of our index is now cached

ทั้งหมดในทุกนี้แสดงให้เห็นว่าการจัดทำดัชนีและตารางสามารถเก็บไว้ทีละหน้าดังนั้นคำตอบสำหรับ(2)เป็นNO

และตารางสุดท้ายเพื่อแสดงตารางชั่วคราวที่ไม่ถูกแคชที่นี่:

CREATE TEMPORARY TABLE tmp_cache_test AS 
SELECT * FROM index_cache_test ORDER BY id FETCH FIRST 20000 ROWS ONLY;

EXPLAIN (ANALYZE, BUFFERS) SELECT id FROM tmp_cache_test ORDER BY id;

-- checking the buffer cache now shows no sign of the temp table

1
+1 คำตอบที่ดีมาก ทำให้รู้สึกว่าตารางอุณหภูมิที่อยู่ใน RAM ไม่ได้ถูกแคช ฉันสงสัยว่าแม้ว่าการแคชจะเกิดขึ้นทันทีที่โต๊ะ temp รั่วไหลไปยังดิสก์ (เพราะขาดความเพียงพอtemp_buffers) - สำหรับทั้งตารางหรือเพียงบางส่วนบนดิสก์ ฉันคาดว่าหลัง อาจเป็นการทดสอบที่น่าสนใจ ..
Erwin Brandstetter

9

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

คุณสามารถมีดัชนี 0.1% ในหน่วยความจำหรือ 100% ของดัชนีได้อย่างง่ายดาย แนวคิดที่ว่า "ดัชนี" ที่เหมาะสมที่สุดส่วนใหญ่จะอยู่ในแคชตลอดเวลา "จะล้มลงอย่างหนักเมื่อคุณมีคิวรีที่สัมผัสเฉพาะเซ็ตย่อยของตาราง ตัวอย่างทั่วไปคือถ้าคุณมีข้อมูลที่มุ่งเน้นเวลา บ่อยครั้งที่การนำทางส่วนท้ายของตารางโดยทั่วไปมักไม่ค่อยได้เห็นประวัติเก่า ที่นั่นคุณอาจพบบล็อกดัชนีทั้งหมดที่จำเป็นในการนำทางไปยังและรอบ ๆ จุดสิ้นสุดล่าสุดในหน่วยความจำในขณะที่มีน้อยมากที่ต้องการนำทางระเบียนก่อนหน้านี้ที่นั่น

ส่วนที่มีความซับซ้อนของการนำไปใช้งานนั้นไม่ได้เป็นวิธีการที่บล็อกเข้าไปในบัฟเฟอร์แคช เป็นกฎเกี่ยวกับเมื่อพวกเขาออกไป My Inside ในการพูดคุยแคชบัฟเฟอร์ของ PostgreSQLและแบบสอบถามตัวอย่างที่รวมอยู่ที่นั่นสามารถช่วยให้คุณเข้าใจสิ่งที่เกิดขึ้นที่นั่นและดูสิ่งที่เกิดขึ้นจริงบนเซิร์ฟเวอร์ที่ใช้งานจริง มันอาจจะแปลกใจ มีมากขึ้นในทุกหัวข้อเหล่านี้ในหนังสือPostgreSQL 9.0 High Performanceของฉันด้วย

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

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