2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

プリザンターのDB容量が増えてしまったときの対処案

Posted at

プリザンターが止まった!

/の容量(26G)が100%のため、プリザンターが止まってしまいました。
容量を削減すべく、下記手順にて調査、削除したところ、15G程度に減りましたので、自分の備忘のためにも手順をまとめておきます。

なお、環境は下記通りです。

  • Rocky Linux
  • Pleasanter 1.4.3.1
  • PostgreSQL

要約

  • どのファイルが容量を喰っているかを確認
  • Syslogsテーブルのデータを削除
  • Results_historyテーブル10以上の履歴データを削除

Results_historyテーブルのデータ削除はマニュアルには記載がないので、十分に検証してから実施してください。

調査

$ sudo du -h --max-depth=1 /

どのファイルが容量を喰っているかが分かります。今回はpostgresqlが犯人でした。

次に、どのテーブルの容量が多いかを確認します。

$ sudo -u postgres psql
# SELECT oid, datname FROM pg_database WHERE oid = 16384;
# \c Implem.Pleasanter
# SELECT
  relname AS table,
  pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;

Results_historyが20G以上容量あり、Syslogが7G以上でした。

Implem.Pleasanter=# SELECT
  count(*) AS rows,
  min("Results_history"."CreatedTime"),
  max("Results_history"."CreatedTime")
FROM "Results_history";

SELECT
  count(*) AS rows,
  min("SysLogs"."CreatedTime"),
  max("SysLogs"."CreatedTime")
FROM "SysLogs";

Syslogsテーブルのデータ削除

ログは削除しても安全そうです。下記のマニュアルをもとにデータを削除します。

# truncate table "SysLogs";

Result_historyテーブルのデータを削除する

Verが最新のもののみを残す方針で、まずはselectで対象の確認するSQLを作成するためにいくつかのSQLを作成します。

なお、全行で85万件ほど。レコードの中身の容量もそこそこあるテーブルだったのですが、85万件で26G以上使っているとは・・・履歴をなめてはあかんですね。

Results_historyの100件取得

select DISTINCT ON ("Results_history"."ResultId")
"Results_history"."ResultId", "Results_history"."Ver" 
from "Results_history" 
ORDER BY "Results_history"."ResultId", "Results_history"."Ver"  DESC;
Limit 100;

実際のデータ件数を確認。

SELECT
  COUNT(DISTINCT "Results_history"."ResultId") AS resultid_count
FROM "Results_history";

5000件強でした。

以下のSQLで、ResultIdごとの履歴数と履歴最大、最小を取得します。

SELECT
  "Results_history"."ResultId",
  COUNT(*) AS cnt,
  MIN("Results_history"."Ver") AS min_ver,
  MAX("Results_history"."Ver") AS max_ver
FROM "Results_history"
GROUP BY "Results_history"."ResultId"
ORDER BY "Results_history"."ResultId";

最新の1件を残して、ほかのすべての履歴を削除する前提で、まずはSelectで対象行を取得するSQLです。

SELECT
  "t"."ResultId",
  "t"."Ver"
FROM (
  SELECT
    "Results_history"."ResultId" AS "ResultId",
    "Results_history"."Ver"      AS "Ver",
    COUNT(*) OVER (
      PARTITION BY "Results_history"."ResultId"
    ) AS cnt,
    ROW_NUMBER() OVER (
      PARTITION BY "Results_history"."ResultId"
      ORDER BY "Results_history"."Ver" DESC
    ) AS rn
  FROM "Results_history"
) AS "t"
WHERE "t".cnt > 1   -- Verが1件しかないResultId(=rnが1つしかない塊)を除外
  AND "t".rn > 1;   -- 各ResultIdの最大Ver(=rn=1)以外を抽出

履歴を最新10件は残す方針に変更

おそらくバッチファイルでUpsertしている行の履歴がたまっていると思われます。バッチを実行してもカラムはほぼ変更がない中で履歴が生成してしまうので、バッチ分は削除しても影響は少ないと思われました。ただ、画面から入力したものについては残しておきたいため、履歴全削除は少し横暴と判断し、最新10件の履歴は残すことにしました。

SELECT
  "t"."ResultId",
  "t"."Ver"
FROM (
  SELECT
    "Results_history"."ResultId" AS "ResultId",
    "Results_history"."Ver"      AS "Ver",
    ROW_NUMBER() OVER (
      PARTITION BY "Results_history"."ResultId"
      ORDER BY "Results_history"."Ver" DESC
    ) AS rn
  FROM "Results_history"
) AS "t"
WHERE "t".rn > 10;

削減率の確認

削除率を表示するSQLです。

WITH t AS (
  SELECT
    ROW_NUMBER() OVER (
      PARTITION BY "Results_history"."ResultId"
      ORDER BY "Results_history"."Ver" DESC
    ) AS rn
  FROM "Results_history"
)
SELECT
  COUNT(*) AS total_rows,
  SUM(CASE WHEN rn > 10 THEN 1 ELSE 0 END) AS deletable_rows,
  ROUND(
    SUM(CASE WHEN rn > 10 THEN 1 ELSE 0 END)::numeric
    * 100 / COUNT(*),
    2
  ) AS reduction_percent
FROM t;

削減率は、最新版のみ残す→99%、10件残す場合は96%でした。

DBのバックアップ

$ su - postgres
# pg_dump -Fc Implem.Pleasanter > /backup/Implem.Pleasanter.dump

削除するSQL

DELETE FROM "Results_history"
WHERE ("ResultId", "Ver") IN (
  SELECT
    "ResultId",
    "Ver"
  FROM (
    SELECT
      "Results_history"."ResultId" AS "ResultId",
      "Results_history"."Ver"      AS "Ver",
      ROW_NUMBER() OVER (
        PARTITION BY "Results_history"."ResultId"
        ORDER BY "Results_history"."Ver" DESC
      ) AS rn
    FROM "Results_history"
  ) t
  WHERE t.rn > 10
);

件数が多くて削除までに時間がかかったため、削除対象件数を設定できるよう改良しました。

削除する件数を指定できる改良版SQL

削除する件数を指定できるSQLです。LIMIT 10000をで調整することができます。

# WITH target AS (
  SELECT
    t."ResultId",
    t."Ver"
  FROM (
    SELECT
      rh."ResultId",
      rh."Ver",
      ROW_NUMBER() OVER (
        PARTITION BY rh."ResultId"
        ORDER BY rh."Ver" DESC
      ) AS rn
    FROM "Results_history" rh
  ) t
  WHERE t.rn > 10
  ORDER BY t."ResultId", t."Ver"
  LIMIT 10000
)
DELETE FROM "Results_history" rh
USING target
WHERE rh."ResultId" = target."ResultId"
  AND rh."Ver"      = target."Ver";

10万件に指定すると、3~5分の間で削除が完了しました。

メモ:削除した時間を取得する方法

削除件数を調整の参考にするため、実行時間を取得したかったので、下記コマンドを実行します。

# \timing on

その他トピックス

deleteが終わったら

まずは通常の VACUUM(オンライン)を実施します。テーブル内の死んだ行を回収し、再利用可能にしますが、OSの空き容量(dfで表示される値)は基本戻りません。

# VACUUM (ANALYZE) "Results_history";

“OSの空き容量も戻したい”場合は、VACUUM FULL はテーブル作り直し=長時間の排他ロックになります。

# VACUUM FULL "Results_history";

バックアップを圧縮しながら実施

バックアップ

$ pg_dump -Fc -Z9 Implem.Pleasanter > /backup/Implem.Pleasanter.dump

-Fc
→ カスタムフォーマット(=内部的に圧縮される)

-Z9
→ 圧縮レベル最大(0〜9)

レストア

目的 コマンド
通常復元 pg_restore -d DB dump
安全に入れ替え pg_restore --clean --if-exists -d DB dump
DBごと再作成 pg_restore --create -d postgres dump
高速 pg_restore -j N
中身確認 pg_restore -l

復元後は必ずANALYZE;を実行してください。

リモートに保存

バックアップデータをSSHを使用してリー元に保存するとサーバーの容量を圧迫しません。

$ pg_dump -Fc -Z9 Implem.Pleasanter \
  | ssh user@backup-server "cat > /backup/Implem.Pleasanter.dump"

まとめ

プライベートの環境だったので大ごとにはならなかったですが、普段から容量は気にしておきましょう。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?