PostgreSQL Dizini Önbelleğe Alma


16

PostgreSQL'de dizinlerin nasıl önbelleğe alındığına dair 'lay' açıklamalarını bulmakta zorluk çekiyorum, bu yüzden bu varsayımların herhangi birinde veya hepsinde bir gerçeklik kontrolü istiyorum:

  1. Sıralar gibi PostgreSQL dizinleri diskte yaşar, ancak önbelleğe alınabilir.
  2. Bir dizin tamamen önbellekte olabilir veya hiç olmayabilir.
  3. Önbelleğe alınıp alınmayacağı, ne sıklıkta kullanıldığına bağlıdır (sorgu planlayıcı tarafından tanımlandığı gibi).
  4. Bu nedenle 'mantıklı' indekslerin çoğu her zaman önbellekte olacaktır.
  5. Dizinler buffer cache, satırlarla aynı önbellekte ( ?) Yaşar ve bu nedenle dizin tarafından kullanılan önbellek alanı satırlar tarafından kullanılamaz.


Bunu anlama motivasyonum , kısmi dizinlerin verilerin çoğuna asla erişilmeyeceği tablolarda nerede kullanılabileceğini önerdiğim başka bir sorudan kaynaklanıyor .

Bunu yapmadan önce, kısmi bir endeks kullanmanın iki avantaj sağladığı açıktır:

  1. Önbellekteki dizinin boyutunu küçültür ve önbellekteki satırlar için daha fazla alan açarız.
  2. B-Ağacının boyutunu küçülterek daha hızlı sorgu yanıtı veririz.

4
Kısmi bir dizin kullanmak, yalnızca verilerin büyük bir kısmına nadiren erişildiğinde değil, aynı zamanda belirli değerlerin çok yaygın olduğu durumlarda da yararlıdır. Bir değer çok yaygın olduğunda, planlayıcı dizin yerine yine de bir tablo taraması kullanacaktır, böylece dizindeki değer dahil olmak hiçbir amaca hizmet etmez.
Eelke

Yanıtlar:


19

Pg_buffercache ile biraz oynayarak , bazı sorularınıza cevap alabilirim.

  1. Bu oldukça açıktır, ancak (5) sonuçları da cevabın EVET olduğunu göstermektedir.
  2. Bunun için henüz iyi bir örnek oluşturmadım, şimdilik hayırdan daha fazla evet :) (Aşağıdaki düzenlememe bakın, cevap HAYIR .)
  3. Planlamacı endeks kullanıp kullanmayacağına kim karar verdiğinden, EVET diyebiliriz , önbelleğe almaya karar verir (ancak bu daha karmaşıktır)
  4. Önbelleğe alma işleminin kesin ayrıntıları kaynak kodundan türetilebilir, bu konu dışında bu konuda çok fazla bulamadım (ayrıca yazarın cevabına da bakın). Ancak, bunun basit bir evet veya hayırdan çok daha karmaşık olduğundan eminim. (Yine, benim düzenlememden biraz fikir edinebilirsiniz - önbellek boyutu sınırlı olduğundan, bu 'mantıklı' dizinler kullanılabilir alan için rekabet eder. Çok fazlalarsa, birbirlerini önbellekten tekmeleyeceklerdir - bu yüzden cevap HAYIR . )
  5. Gösterileri ile basit bir sorgu olarak pg_buffercache, cevap kesin bir EVET . Geçici tablo verilerinin burada önbelleğe alınmadığına dikkat etmek gerekir .

DÜZENLE

Yeremya Peschka'nın tablo ve indeks depolama hakkındaki müthiş makalesini buldum . Oradan gelen bilgilerle (2) de cevap verebilirim . Küçük bir test yaptım, böylece bunları kendiniz kontrol edebilirsiniz.

-- 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

Sonuçta, indeksler ve tablolar sayfa sayfa önbelleğe alınabilir bu gösterileri dolayısıyla için cevap (2) olduğu YOK .

Ve geçici tabloların burada önbelleğe alınmadığını gösteren son bir tablo:

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 Çok güzel bir cevap. RAM'de yaşayan geçici tabloların önbelleğe alınmadığı mantıklıdır. Acaba, önbellekleme geçici tablolar diske dökülür dökülmez gerçekleşir (yeterli eksikliği nedeniyle temp_buffers) - tüm tablo veya sadece disk parçası için. Ben ikincisini beklerdim. İlginç bir test olabilir ..
Erwin Brandstetter

9

Dizin sayfaları, bir sorgu, bir sorguyu yanıtlamak için gereken tablo verilerinin miktarını azaltmak için yararlı olacağına karar verdiğinde getirilir. Sadece dizin için yerine getirilen bloklar okunur. Evet, tablo verilerinin depolandığı aynı paylaşılan_buffers havuzuna giderler. Her ikisi de ikinci bir önbellek katmanı olarak işletim sistemi önbelleği tarafından desteklenir.

Bellekte% 0.1 veya% 100 bir dizine kolayca sahip olabilirsiniz. Yalnızca bir tablonun alt kümesine dokunan sorgularınız olduğunda "" mantıklı "dizinlerin her zaman önbellekte olacağı fikri zorlaşır. Yaygın bir örnek, zaman odaklı verilerinizdir. Çoğu zaman bunlar genellikle tablonun son ucunda gezinir ve nadiren eski tarihi görür. Orada bellekte son ucuna gitmek ve çevresinde son endeks blokları bulabilirsiniz, önceki kayıtlarda gezinmek için çok az ihtiyaç vardır.

Uygulamanın karmaşık kısımları, blokların tampon önbelleğe nasıl girdiği değildir. Ne zaman ayrıldıklarına dair kurallar. Benim İçinde PostgreSQL Tampon Önbellek konuşma ve örnek sorgusu olduğunu orada ne olup bittiğini anlamalarına yardımcı ve gerçekten bir üretim sunucusuna biriken ne olduğunu görebilirsiniz dahil. Şaşırtıcı olabilir. PostgreSQL 9.0 Yüksek Performans kitabımda tüm bu konularla ilgili çok daha fazlası var .

Kısmi dizinler, dizinin boyutunu azalttığı için faydalı olabilir ve bu nedenle, her ikisi de gezinmek ve diğer şeyleri önbelleğe almak için daha fazla RAM bırakmak için daha hızlıdır. Dizinde gezinmeniz, dokunduğunuz parçalar her zaman RAM'de olacak şekilde ise, yine de gerçek bir iyileştirme almayabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.