PostgreSQL 인덱스 캐싱


16

PostgreSQL에서 인덱스가 캐시되는 방식에 대한 '레이'설명을 찾는 데 어려움을 겪고 있으므로 다음 가정 중 하나 또는 모두에 대한 실제 확인을 원합니다.

  1. 행과 같은 PostgreSQL 인덱스는 디스크에 있지만 캐시 될 수 있습니다.
  2. 인덱스는 전적으로 캐시에 있거나 전혀 없을 수 있습니다.
  3. 캐시 여부는 쿼리 플래너에서 정의한대로 사용 빈도에 따라 다릅니다.
  4. 이러한 이유로 대부분의 '합리적인'인덱스는 항상 캐시에있게됩니다.
  5. 인덱스 buffer cache는 행과 동일한 캐시 ( ?)에 있으므로 인덱스에서 사용하는 캐시 공간을 행에 사용할 수 없습니다.


이것을 이해하려는 동기 는 대부분의 데이터에 액세스 할 수없는 테이블에서 부분 인덱스를 사용할 수 있다고 제안한 또 다른 질문 에서 비롯 됩니다.

이를 수행하기 전에 부분 인덱스를 사용하면 다음과 같은 두 가지 이점이 있습니다.

  1. 캐시의 인덱스 크기를 줄이면 캐시의 행 자체에 더 많은 공간을 확보 할 수 있습니다.
  2. B-Tree의 크기를 줄이면 쿼리 응답 속도가 빨라집니다.

4
부분 인덱스를 사용하면 많은 양의 데이터에 거의 액세스 할 수 없을 때뿐만 아니라 특정 값이 매우 일반적인 경우에도 유용합니다. 값이 매우 일반적인 경우 플래너는 인덱스 대신 테이블 스캔을 사용하므로 인덱스에 값을 포함하면 아무런 목적이 없습니다.
Eelke

답변:


19

pg_buffercache 로 조금만 연주하면 몇 가지 질문에 대한 답변을 얻을 수 있습니다.

  1. 이것은 명백하지만 (5) 의 결과는 또한 `` 예 '' 라고 대답합니다.
  2. 나는 이것에 대한 좋은 예를 아직 세우지 않았다. 지금은 아니오보다 그렇다. :) (아래 편집 참조, 대답은 NO )
  3. 플래너는 인덱스 사용 여부를 결정하는 사람이므로 YES 라고 말하면 캐싱을 결정합니다 (그러나 더 복잡합니다)
  4. 캐싱에 대한 정확한 세부 사항은 소스 코드에서 파생 될 수 있습니다.이 주제를 제외 하고는 주제에서 너무 많이 찾을 수 없었습니다 (저자의 답변 참조 ). 그러나 나는 이것이 단순한 예 또는 아니오보다 훨씬 더 복잡하다는 것을 확신합니다. (내 편집에서 캐시 크기가 제한되어 있기 때문에 해당 '지능적'인 인덱스는 사용 가능한 공간과 경쟁합니다. 너무 많은 경우 캐시에서 서로를 쫓아 낼 수 있으므로 대답은 오히려 NO 입니다. )
  5. show가 포함 된 간단한 쿼리로서 pg_buffercache정답은 YES 입니다. 임시 테이블 데이터 여기에 캐시 되지 않습니다 .

편집하다

테이블 및 인덱스 스토리지에 대한 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_buffers전체 테이블 또는 디스크의 일부에 대해 임시 테이블이 디스크에 유출되는 즉시 캐싱이 발생하는지 궁금합니다 . 나는 후자를 기대할 것이다. 흥미로운 시험이 될 수 있습니다 ..
Erwin Brandstetter

9

쿼리가 쿼리에 응답하는 데 필요한 테이블 데이터의 양을 줄이는 데 유용하다고 판단하면 인덱스 페이지가 페치됩니다. 인덱스 블록 만 탐색하여 읽습니다. 예, 테이블 데이터가 저장된 동일한 shared_buffers 풀로 이동합니다. 둘 다 운영 체제 캐시에 의해 두 번째 캐싱 계층으로 지원됩니다.

메모리에 0.1 %의 인덱스 또는 100 %의 인덱스를 쉽게 가질 수 있습니다. 테이블의 하위 집합에만 적용되는 쿼리가있는 경우 대부분의 "가상적인"인덱스가 항상 캐시에있을 것이라는 생각은 어려워집니다. 일반적인 예는 시간 지향 데이터가있는 경우입니다. 종종 그것들은 일반적으로 테이블의 최근 끝을 탐색하여 오래된 역사를 거의 보지 않습니다. 거기에서 메모리의 최근 끝으로 이동하는 데 필요한 모든 인덱스 블록을 찾을 수 있지만 이전 레코드를 탐색하는 데 필요한 것은 거의 없습니다.

구현의 복잡한 부분은 블록이 버퍼 캐시에 들어가는 방식이 아닙니다. 그들이 떠날 때의 규칙입니다. 내 내부 PostgreSQL 버퍼 캐시 대화 및 여기에 포함 된 샘플 쿼리를 통해 진행 상황을 이해하고 프로덕션 서버에서 실제로 누적되는 내용을 확인할 수 있습니다. 놀랍습니다. PostgreSQL 9.0 High Performance 책에서도 이러한 모든 주제에 대해 더 많은 정보를 얻을 수 있습니다.

부분 인덱스는 인덱스 크기를 줄이므로 유용 할 수 있으므로 다른 항목을 캐싱하기 위해 RAM을 더 빨리 탐색하고 더 많이 남겨 둘 수 있습니다. 인덱스 탐색에서 터치하는 부분이 항상 RAM에있는 경우 어쨌든 실제로 개선되지는 않을 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.