PostgreSQLインデックスキャッシング


16

PostgreSQLでのインデックスのキャッシュ方法に関する「わかりやすい」説明を見つけるのが難しいので、これらの仮定のいずれかまたはすべてを現実的にチェックしたいと思います。

  1. 行のようなPostgreSQLインデックスはディスク上に存在しますが、キャッシュされる場合があります。
  2. インデックスは完全にキャッシュ内にある場合とまったくない場合があります。
  3. キャッシュされるかどうかは、それが使用される頻度に依存します(クエリプランナーが定義)。
  4. このため、ほとんどの「賢明な」インデックスは常にキャッシュに置かれます。
  5. インデックスbuffer cacheは行と同じキャッシュ(?)に存在するため、インデックスで使用されるキャッシュスペースは行で使用できません。


これを理解する動機は、データの大部分が決してアクセスされないテーブルで部分インデックスを使用できると示唆された別の質問に続いています。

これを実行する前に、部分インデックスを使用すると2つの利点が得られることを明確にしたいと思います。

  1. キャッシュ内のインデックスのサイズを小さくし、キャッシュ内の行自体により多くのスペースを解放します。
  2. Bツリーのサイズを小さくして、クエリの応答を高速化します。

4
部分インデックスの使用は、データの大部分がほとんどアクセスされない場合だけでなく、特定の値が非常に一般的である場合にも役立ちます。値が非常に一般的な場合、プランナーはインデックスの代わりにテーブルスキャンを使用するため、インデックスに値を含めることは意味がありません。
-Eelke

回答:


19

pg_buffercacheで少し遊んで、あなたの質問のいくつかに対する答えを得ることができました。

  1. これは非常に明白ですが、(5)の結果も答えがYESであることを示しています
  2. 私はまだこれの良い例をセットアップしていません、今のところはノーよりもイエスです:)(以下の私の編集を参照してください、答えはNOです)
  3. プランナーがインデックスを使用するかどうかを決定するのはYESであるため、キャッシュを決定します(ただし、これはより複雑です)
  4. キャッシングの正確な詳細は、ソースコードから派生することができ、私は除いて、このトピックにあまり見つけることができなかったこの1(作者の参照の答えを、あまりにも)。ただし、これも単純なyesまたはnoよりもはるかに複雑であると確信しています。(繰り返しますが、私の編集から、あなたはいくつかのアイデアを得ることができます-キャッシュサイズが制限されるため、それらの「賢明な」インデックスが利用可能なスペースを競う、彼らはあまりにも多くの場合、それらはキャッシュからお互いをキックしないだろう- 。答えはかなりあるのでNO。 )
  5. 簡単なクエリが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です。

そして、ここで一時的なテーブルがキャッシュされないことを説明する最後の1つです。

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)-テーブル全体またはディスク上の一部のみに対して。私は後者を期待しています。興味深いテストかもしれません..
アーウィンブランドステッター

9

インデックスページは、クエリがクエリに応答するために必要なテーブルデータの量を削減するのに役立つと判断したときにフェッチされます。それを達成するためにナビゲートされたインデックスのブロックのみが読み込まれます。はい、テーブルデータが保存されているのと同じshared_buffersプールに入ります。どちらも、キャッシュの2番目のレイヤーとしてオペレーティングシステムのキャッシュによってサポートされています。

メモリ内にインデックスの0.1%または100%を簡単に保持できます。ほとんどの「 '賢明な'インデックスは常にキャッシュ内に存在する」という考えは、テーブルのサブセットのみを参照するクエリがある場合、大いに落ちます。一般的な例は、時間指向のデータがある場合です。多くの場合、それらは通常、テーブルの最近の終わりをナビゲートし、古い歴史をめったに見ません。そこには、メモリ内の最近のエンドにナビゲートするために必要なすべてのインデックスブロックがありますが、以前のレコードをナビゲートするために必要なインデックスブロックはほとんどありません。

実装の複雑な部分は、ブロックがバッファキャッシュに入る方法ではありません。彼らが去るときのルールです。私のPostgreSQLバッファキャッシュの話とそこに含まれるサンプルクエリは、そこで何が起こっているのかを理解し、本番サーバーで実際に何が蓄積されているかを知るのに役立ちます。驚くかもしれません。私のPostgreSQL 9.0 High Performanceブックには、これらすべてのトピックに関する詳細が記載されています

部分インデックスは、インデックスのサイズを縮小するので役立ちます。そのため、ナビゲートするのが速くなり、他のものをキャッシュするためにより多くのRAMを残します。とにかく、インデックスのナビゲーションで、触れる部分が常にRAMにある場合、実際の改善は得られないかもしれません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.