DBA:死んだ魂を片付ける

PostgreSQLで長いクエリや記述が不十分なクエリを実行すると、突然のプロセスクラッシュやサーバー全体のクラッシュなど、さまざまな不快なことが発生することがあります





このような場合、「死んだ魂」がメディアに残る可能性があります-中間データの一時的なストレージとしてプロセス中に作成されたファイル(非常に大きい場合もありますが、データベースの他の部分とサイズがかなり匹敵します)





このデータはもはや誰も必要とせず、誰も使用することはできませんが、サーバーはPlyushkinのようにそれを取り除くために急いでいません。





今日は、それらがどのように見つけられ、痛みを伴わずに「クリーンアップ」されるかを見ていきます。





一時バッファを探しています

発生する問題の最初のカテゴリは、必要な量のメモリがwork_memに収まらない場合に、プランノードの実行中に一時的にディスク領域を使用することです





このような効果を得るのは非常に簡単です-大きすぎる再帰制限を設定または選択するのを忘れる





explain (analyze, buffers)
WITH RECURSIVE T AS (
  SELECT
    0 i
  , '' s
UNION ALL
  SELECT
    i + 1
  , repeat('a', i + 1)
  FROM
    T
  WHERE
    i < 1e4 -- 10k 
)
TABLE T ORDER BY s DESC LIMIT 1;
      
      



[ explain.tensor.ru]





, T



- , temp written



:





->  CTE Scan on t  (actual time=0.008..374.157 rows=10001 loops=1)
      Buffers: temp written=6126
      
      



, - :





SELECT pg_backend_pid();
-- 15004 -  PID ,    

explain (analyze, buffers)
WITH RECURSIVE T AS (
  SELECT
    0 i
  , '' s
UNION ALL
  SELECT
    i + 1
  , repeat('a', i + 1)
  FROM
    T
  WHERE
    i < 1e5 -- 100k 
)
TABLE T ORDER BY s DESC LIMIT 1;
      
      



kill -9 15004
      
      



ひどく「殺された」クライアントプロセスは、ポストマスターとその背後にあるPostgreSQLサーバー全体を引っ張る
"" postmaster PostgreSQL-

- . 4GB - ?





pg_ls_tmpdir:





SELECT * FROM pg_ls_tmpdir();
      
      



 name            |  size      |  modification
pgsql_tmp15004.0 | 1073741824 | 2021-05-12 10:46:10+03
pgsql_tmp15004.1 | 1073741824 | 2021-05-12 10:46:38+03
pgsql_tmp15004.2 | 1073741824 | 2021-05-12 10:47:06+03
pgsql_tmp15004.3 |  958078976 | 2021-05-12 10:47:31+03
      
      



PostgreSQL 12, , pg_ls_dir



<data>/base/pgsql_tmp



- , , .





PID , . , , , :





WITH dir AS (
  SELECT
    current_setting('data_directory') || '/base/pgsql_tmp' dir
)
, ls AS (
  SELECT
    *
  , pg_ls_dir(dir) fn
  FROM
    dir
)
, tmp AS (
  SELECT
    *
  , regexp_replace(fn, '^pgsql_tmp(\d+).*$', '\1')::integer pid
  , (pg_stat_file(dir || '/' || fn)).*
  FROM
    ls
)
SELECT
  dir || '/' || fn
FROM
  tmp
LEFT JOIN
  pg_stat_activity sa
    USING(pid)
WHERE
  sa IS NOT DISTINCT FROM NULL;
      
      



. , "" pg_terminate_backend(pid)



, "", "" .





TEMPORARY TABLE

CREATE TEMPORARY TABLE x AS
SELECT
  i
, repeat('a', i::integer) s
FROM
  generate_series(1, 1e5) i;
      
      



pg_temp_5



:





SELECT current_schemas(true);
-- {pg_temp_5,pg_catalog,public}
      
      



pg_temp



- TABLE x



, TABLE pg_temp.x



TABLE pg_temp_5.x



, .





, "", temp buffers



, pg_class



:





SELECT
  oid
, relnamespace::regnamespace
, relname
, relfilenode
FROM
  pg_class
WHERE
  relnamespace::regnamespace::text ~ '^pg(_toast)?_temp';
      
      



 oid  |  relnamespace   |  relname             |  relfilenode
66112 | pg_toast_temp_5 | pg_toast_66109       | 66112
66114 | pg_toast_temp_5 | pg_toast_66109_index | 66114
66109 | pg_temp_5       | x                    | 66109
      
      



Tom Lane 2003:





> What is the origin of these schemas? local temporary tables? sorts?





Right, they're made to hold temporary tables. The first time a givenbackend does CREATE TEMP TABLE, it looks for a pg_temp_n schema, and makes it if it's not there. On shutdown, it removes the temp tables, but it seemed like a waste of cycles to remove the pg_temp_n schema itself.





(ObTrivialFact: the 'n' is the backend's pgproc slot number, so it's known not to be in use by any concurrently running backend. But it will certainly be used again in future.)





, . , ?





temp buffers, , , :





WITH dir AS (
  SELECT
    current_setting('data_directory') || '/base/' || oid dir
  FROM
    pg_database
  WHERE
    datname = current_database()
)
, ls AS (
  SELECT
    *
  , pg_ls_dir(dir) fn
  FROM
    dir
)
SELECT
  *
FROM
  ls
WHERE
  fn ~ '^t';
      
      



 dir                |  fn
.../data/base/16393 | t5_66109
.../data/base/16393 | t5_66112
.../data/base/16393 | t5_66114
      
      



t<temp schema N>_<temp object OID>



. "" , , pg_class



.





, VACUUM FULL



, , . autovacuum



:





LOG: autovacuum: found orphan temp table "tst.pg_temp_5.x"
      
      



, , - .





, "" , , "" :





WITH dir AS (
  SELECT
    current_setting('data_directory') || '/base/' || oid dir
  FROM
    pg_database
  WHERE
    datname = current_database()
)
, ls AS (
  SELECT
    *
  , pg_ls_dir(dir) fn
  FROM
    dir
)
, lsid AS (
  SELECT
    *
  , (pg_stat_file(dir || '/' || fn)).*
  FROM
    ls
  WHERE
    fn ~ '^t'
)
, sch AS (
  SELECT DISTINCT
    regexp_replace(fn, '^t(\d+)_.*$', '\1') sch
  FROM
    lsid
  WHERE
    modification < (
      SELECT
        stats_reset
      FROM
        pg_stat_database
      WHERE
        datid = 0
    )
)
SELECT
  string_agg('DROP SCHEMA ' || nspname || ' CASCADE;', E'\n') s
FROM
  sch
JOIN
  pg_namespace nsp
    ON nsp.nspname IN ('pg_temp_' || sch, 'pg_toast_temp_' || sch);
      
      



, :





DROP SCHEMA pg_temp_5 CASCADE;
DROP SCHEMA pg_toast_temp_5 CASCADE;
      
      






All Articles