web-dev-qa-db-ja.com

pg_prewarmを使用してX行の最新の行をキャッシュにロードする

お客様が「初めて、早朝に実行する...」となると、非常に遅い大きなクエリがあります。

だから、私は pg_prewarm を使用して、PGのバッファーキャッシュへのロードを使用したい、または上記のクエリで使用されているいくつかのテーブルから特定の量または最後にアクセスされた行(挿入、更新、または削除)を使用したいと思います。

さらに、「ウォームアップ」がPGのキャッシュを超えていないことを確認する必要があります(Shared_buffersの設定が間違っていると思いますか、それとも間違っていますか)。

SELECT pg_prewarm(
    'mytable',
    -- "pre warm" last 1000 pages
    first_block := ( 
        SELECT pg_relation_size('mytable') / current_setting('block_size')::int4 - 1000
    )
);

質問#1:このアプローチは理にかなっていますか?

トリックは、pg_prewarmは特定の数のページしかロードできないため、「特定のテーブルのページにあるライブ行の数」を計算する必要があります。

-- show some settings
SELECT current_setting('block_size')::int4 AS page_size_bytes; -- 8192
SHOW shared_buffers; -- 512 MB


-- https://www.postgresql.org/docs/current/static/pgstattuple.html
--CREATE EXTENSION pgstattuple;
-- find out live row size and live rows per page
SELECT 'mytable'AS table_name, pg_size_pretty(Tuple_len / Tuple_count) AS live_row_size, 8192.00 / (Tuple_len / Tuple_count) AS live_rows_per_page, * FROM pgstattuple('public.mytable')

--"table_name","live_row_size","live_rows_per_page","table_len","Tuple_count","Tuple_len","Tuple_percent","dead_Tuple_count","dead_Tuple_len","dead_Tuple_percent","free_space","free_percent"
--"studies","1286 bytes",6.3701399688958009,652697600,462123,594431269,91.07,0,0,0,52329672,8.02

質問#2:上記のクエリは正しいですか?これは「ページごとのライブ行」を取得する適切な方法ですか?質問#3:上記のクエリから取得したlive_row_sizeが結果と同じではありません この答え (アーウィンによる)で言及されています。私は何か間違ったことをしていますか?

Live_rows_per_pageに基づいて、pg_prewarmを変更して、10,000行(6.37 x 10,000)を含む十分な最後のXXXXページをロードできます。

SELECT pg_prewarm(
    'mytable',
    -- "pre warm" pages of the last 10,000 rows for 'mytable'
    first_block := ( 
        SELECT pg_relation_size('mytable') / current_setting('block_size')::int4 - 63700
    )
);

Update#1質問#3について、mytableのクエリを実行すると次のようになります... pgstatupleの出力は同じアイテムがリストされていないためか、違いますが、よくわかりません...

"what","bytes/ct","bytes_pretty","bytes_per_row"
"core_relation_size",652697600,"622 MB",1412
"visibility_map",16384,"16 kB",0
"free_space_map",180224,"176 kB",0
"table_size_incl_toast",1101955072,"1051 MB",2384
"indexes_size",508289024,"485 MB",1099
"total_size_incl_toast_and_indexes",1610244096,"1536 MB",3484
"live_rows_in_text_representation",1138946462,"1086 MB",2464
"------------------------------",<NULL>,"<NULL>",<NULL>
"row_count",462123,"<NULL>",<NULL>
"live_tuples",0,"<NULL>",<NULL>
"dead_tuples",3,"<NULL>",<NULL>
2
zam6ak

質問3に対処する

上記のクエリで取得したlive_row_sizeは、この回答で述べた結果とは異なります(Erwinによる)。私は何か間違ったことをしていますか?

あなたが持っている:

SELECT pg_size_pretty(Tuple_len / Tuple_count) AS live_row_size
FROM pgstattuple('public.mytable');

マニュアルでは以下を定義しています:

Tuple_len ...ライブタプルの全長(バイト単位)
Tuple_count ...ライブタプルの数

中程度の長さのライブタプルを計算します。データページをRAMにフェッチするときにPostgresがライブタプルのみを抽出することを期待しているようですが、それはそれが機能する方法ではありません。 Postgresは完全なページをRAMに読み込むだけで、含まれている可能性のあるすべての無効なタプルを含みます

したがって、次の式はではなく計算 "ページごとのライブ行"を計算します。

8192.00 / (Tuple_len / Tuple_count) AS live_rows_per_page

"ライブタプルのみを含むデータページあたりの仮想行の仮想最大値"を計算します。

アップデート#1への対応

なぜサイズが異なるのですか? 参考回答 では、私はこれで主導しています:

これは、「行のサイズ」を測定するさまざまな方法が非常に異なる結果につながる可能性があることを示します。それはすべて、正確に測定したいものに依存します。

あなたの調査結果はそれを裏付けているようです-あなたが正確に何を比較するのか私にはわかりませんが。

参照される回答は少し時代遅れです。いくつかの詳細を改善し、比較のために pgstattuple から番号を追加しました。データベースごとにモジュールを1回インストールする必要があります(まあ、もちろんそれを持っていますが、おそらく他の読者もいます)。

CREATE EXTENSION pgstattuple;

次に:

SELECT l.what, l.nr AS "bytes/ct"
     , CASE WHEN is_size THEN pg_size_pretty(nr) END AS bytes_pretty
     , CASE WHEN is_size THEN nr
      / CASE part WHEN 1 THEN NULLIF(x.ct, 0)
                  WHEN 2 THEN NULLIF(st.Tuple_count, 0)
                  WHEN 3 THEN NULLIF(st.dead_Tuple_count, 0)
                  WHEN 4 THEN NULLIF(st.Tuple_count + st.dead_Tuple_count, 0) END
            END AS bytes_per_row
FROM  (
   SELECT min(tableoid)        AS tbl      -- same as 'public.tbl'::regclass::oid
        , count(*)             AS ct
        , sum(length(t::text)) AS txt_len  -- length in characters
   FROM   public.big t                     -- provide table name *once*
   ) x
 , LATERAL pgstattuple(tbl) st             -- also get numbers from pgstattuple 
 , LATERAL (
   VALUES
      (1, false, 'row_count'                           , ct)
    , (1, false, ' ------ DB_obj_size_func -------'    , NULL)
    , (1, true , 'core_relation_size'                  , pg_relation_size(tbl))
    , (1, true , 'visibility_map'                      , pg_relation_size(tbl, 'vm'))
    , (1, true , 'free_space_map'                      , pg_relation_size(tbl, 'fsm'))
    , (1, true , 'table_size_incl_toast'               , pg_table_size(tbl))
    , (1, true , 'indexes_size'                        , pg_indexes_size(tbl))
    , (1, true , 'total_size_incl_toast_and_indexes'   , pg_total_relation_size(tbl))
    , (1, true , 'live_rows_in_text_representation'    , txt_len)
    , (2, false, ' ------ pgstattuple ------------'    , NULL)
    , (2, false, 'live_tuples'                         , st.Tuple_count)
    , (2, false, 'dead_tuples'                         , st.dead_Tuple_count)
    , (2, true , 'total table / live tuples'           , st.table_len)
    , (2, true , 'live table / live tuples'            , st.Tuple_len)
    , (3, true , 'dead table / dead tuples'            , st.dead_Tuple_len)
    , (4, true , 'total table / all tuples'            , st.table_len)
   ) l(part, is_size, what, nr);

簡単な解決策?

クエリに副作用がない場合、最も効率的な方法は、クエリを実行して関連するデータページをRAMにフェッチすることです。

5