Кешування індексів PostgreSQL


16

У мене виникають труднощі з пошуку «закладають» пояснень кешування індексів у PostgreSQL, тому я хотів би перевірити реальність будь-яких або всіх цих припущень:

  1. Індекси PostgreSQL, як і рядки, живуть на диску, але можуть бути кешовані.
  2. Індекс може бути повністю в кеш-пам'яті або зовсім не бути.
  3. За кешами він буде чи ні, залежить від того, як часто він використовується (як визначено планувальником запитів).
  4. З цієї причини більшість «розумних» індексів постійно будуть знаходитись у кеші.
  5. Індекси живуть у тому ж кеші ( buffer cache?), Що й рядки, і тому простір кешу, який використовується індексом, недоступний для рядків.


Моя мотивація для розуміння цього випливає з іншого питання, яке я запитав, де пропонувалося, що часткові індекси можна використовувати в таблицях, де більшість даних ніколи не будуть доступні.

Перш ніж робити це, я хотів би зрозуміти, що використання часткового індексу дає дві переваги:

  1. Ми зменшуємо розмір індексу в кеші, звільняючи більше місця для самих рядків у кеші.
  2. Ми зменшуємо розмір дерева B, що призводить до швидшого відповіді на запит.

4
Використання часткового індексу корисно не лише тоді, коли велика частина даних буде доступна рідко, але і тоді, коли певні значення дуже поширені. Коли значення є дуже поширеним, планувальник використовуватиме сканування таблиці в будь-якому випадку замість індексу, тому включення значення в індекс не виконує жодної мети.
Eelke

Відповіді:


19

Граючи трохи з pg_buffercache , я міг отримати відповіді на деякі ваші запитання.

  1. Це цілком очевидно, але результати (5) також показують, що відповідь ТАК
  2. Я ще маю навести хороший приклад для цього, поки що це більше, ніж ні :) (Дивіться мою редакцію нижче, відповідь - ні .)
  3. Оскільки планувальник вирішує, використовувати індекс чи ні, можна сказати ТАК , він вирішує кешування (але це складніше)
  4. Точні деталі кешування може бути отримані з вихідного коду, я не міг знайти дуже багато на цю тему, крім цього одного (див авторського відповіді , теж). Однак я майже впевнений, що це знову набагато складніше, ніж простий так чи ні. (Знову ж таки, з моєї редагування ви можете отримати уявлення - оскільки розмір кешу обмежений, ці "розумні" індекси змагаються за доступний простір. Якщо їх занадто багато, вони будуть виштовхувати один одного з кеша - тому відповідь скоріше " НІ" . )
  5. Як простий запит із pg_buffercacheшоу, відповідь - остаточний ТАК . Варто зазначити, що дані тимчасової таблиці тут не кешуються.

EDIT

Я знайшов надзвичайну статтю Єремії Пешка про зберігання таблиць та індексів. Маючи інформацію звідти, я міг би відповісти (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) - НІ .

І остаточний для ілюстрації тимчасових таблиць, які тут не є кешованими:

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 Дуже приємна відповідь. Має сенс, що тимчасові таблиці, що живуть в оперативній пам'яті, не кешовані. Мені цікаво, чи не відбувається кешування, як тільки темп-таблиці проливають на диск (за відсутності достатнього temp_buffers) - для всієї таблиці або просто частини на диску. Я б очікував останнього. Можливо, буде цікавим тестом ..
Ервін Брандштеттер

9

Сторінки індексу дістаються, коли запит вирішить, що вони будуть корисні для зменшення кількості даних таблиці, необхідних для відповіді на запит. Лише блоки індексу, які переглядаються, прочитуються. Так, вони переходять у той самий пул спільних_буферів, де зберігаються дані таблиці. Вони також підтримуються кешем операційної системи як другий шар кешування.

Ви можете легко мати 0,1% індексу в пам'яті або 100% його. Ідея про те, що більшість "розумних" індексів буде постійно знаходитися в кеші, "важко падає, коли у вас є запити, які стосуються лише підмножини таблиці. Поширений приклад - якщо у вас є дані, орієнтовані на час. Часто вони зазвичай переглядають останній кінець таблиці, рідко переглядаючи стару історію. Там ви можете знайти в пам'яті всі блоки індексів, необхідні для навігації до останнього кінця та навколо нього, тоді як для навігації по попередніх записах існує дуже мало.

Складні частини реалізації - це не те, як блоки потрапляють в кеш-пам'ять. Це правила про те, коли вони виїжджають. Мої розмови про кеш Buffer Cache PostgreSQL та включені до них зразки запитів можуть допомогти вам зрозуміти, що там відбувається, і побачити, що насправді накопичується на виробничому сервері. Це може дивувати. Про всі ці теми у моїй книзі високої продуктивності PostgreSQL 9.0 є ще багато.

Часткові індекси можуть бути корисними, оскільки вони зменшують розмір індексу, а значить, і швидше орієнтуватися, і залишати більше оперативної пам’яті для кешування інших речей. Якщо ваша навігація по індексу така, що деталі, до яких ви торкаєтесь, завжди знаходяться в оперативній пам’яті, проте це може не придбати справжнього поліпшення.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.