3

問題があり、これまで手がかりが見つかりませんでした。できる限りご説明いたしますが、詳しくはお気軽にお尋ねください!

環境

Windows で Postgres 9.2.4 を使用していますが、ユーザーごとに何らかのクォータ管理を実装する必要があります。

私が読んだ限りでは、そのような組み込み機能はなく、ほとんどの回答はファイル システムのクォータ管理機能を使用することを示しています。

単一のデータベースがあり、各ユーザーは独自のスキーマを持ちます。

私が取ったアプローチには、ユーザーごとに異なるテーブルスペースを持ち、ユーザーごとにテーブルスペースの所有者になることにより、異なる場所にある各ユーザーのデータファイルを分離することが含まれます (したがって、フォルダーごとにクォータ構成を適用できます)。 .

これは私が直面している問題に私を導きました...

問題

テーブルを作成するときに、ユーザーがpg_defaultテーブルスペースを選択してデータを保存できる場合があります。

混乱を招くことに、後でテーブルスペースをユーザーが所有するものに変更し、それを pg_default テーブルスペースに戻そうとすると、パーミッション拒否エラーがスローされます。

シーケンスを明確にするために、いくつかのサンプル コードを示します。

-- Creates the table in the default tablespace
CREATE TABLE test_schema.test_table ( ) 
TABLESPACE pg_default;

-- Changes the tablespace to the one owned by the user
ALTER TABLE test_schema.test_table
SET TABLESPACE user_tablespace;

-- Tries to set back the pg_default tablespace (throws permission denied to pg_default tablespace)
ALTER TABLE test_schema.test_table
SET TABLESPACE pg_default;

これらのコマンドはすべて、管理者権限のないユーザー ログインを使用して実行されました。pg_default テーブルスペースは、postgres ログイン (管理者アカウント) によって所有されています。

私の推測では、pg_default テーブルスペースを使用するように設定されているデータベース テーブルスペースと関係があると思われます。

質問

ユーザーが所有する表領域にのみオブジェクトを作成するように制限することは可能ですか?

4

1 に答える 1

2

ディスク クォータを使用すると、非常に多くの作業が必要になります。実際、PostgreSQL にはおおよその解決策があり、いくつかのマイナーな変更があり、多数のテーブルスペースを作成する必要はありません (すべてのユーザーに独自の名前空間を与えるには、スキーマを使用することをお勧めします)。

この関数pg_total_relation_size(regclass)は、インデックスと TOAST テーブルを含む、テーブルに使用されるディスク容量の合計を示します。したがって、スキャンpg_classして要約します。

CREATE VIEW user_disk_usage AS
  SELECT r.rolname, SUM(pg_total_relation_size(c.oid)) AS total_disk_usage
  FROM pg_class c, pg_roles r
  WHERE c.relkind = 'r'
    AND c.relowner = r.oid
  GROUP BY c.relowner;

これにより、テーブルの場所に関係なく、各所有者が使用するディスク容量の合計がわかります。以下で使用するために、ここではビュー定義として提示されています。

これを適度に正確に機能させるにはVACUUM ANALYZE、データベースを定期的に更新する必要があります。トラフィックの少ない時間帯 (たとえば、毎日午前 3 時から午前 5 時、または日曜日) に実行してから、ユーザー postgres でスケジュールされたジョブを使用します。VACUUM を実行してからクォータ チェックを実行するジョブの関数を作成します。

CREATE FUNCTION user_quota_check() RETURNS void AS $$
DECLARE
  user_data record;
BEGIN
  -- Vacuum the database to get accurate disk use data
  VACUUM FULL ANALYZE;

  -- Find users over disk quota
  FOR user_data IN SELECT * FROM user_disk_usage LOOP
    IF (user_data.total_disk_usage > <<your quota>>) THEN
      EXECUTE 'REVOKE CREATE ON SCHEMA ' || <<user''s schema name>> || ', PUBLIC FROM ' || user_data.rolname;
      -- REVOKE INSERT privileges too, unless you work with BEFORE INSERT triggers on all tables
    END IF;
  END LOOP;
END; $$ LANGUAGE plpgsql;
REVOKE ALL ON FUNCTION user_quota_check() FROM PUBLIC;

所有者が割り当てを超えた場合、REVOKE CREATE関連するすべてのスキーマ (通常は、ユーザーに割り当てられたスキーマとパブリック スキーマのみ) で、新しいテーブルを作成できなくなります。すべてのテーブルでも行う必要がありますREVOKE INSERTが、所有者はすぐに戻ることができるため、これは簡単に回避できGRANT INSERTます。ただし、これは、ユーザーに対するより抜本的なアクションの原因になる可能性があります。上記のような毎日のスイープを使用して、データベース内のすべてのテーブルに挿入前トリガーを作成することをお勧めします。

ユーザーは引き続き SELECT 権限を持っているため、引き続きデータにアクセスできます。さらに興味深いことに、DELETE と TRUNCATE を使用すると、ユーザーはディスク領域を解放し、ロックアウトを修正できます。その後、上記の関数に似たものを使用して権限を回復できます。

CREATE FUNCTION reclaim_disk_space() RETURNS void AS $$
DECLARE
  disk_use bigint;
BEGIN
  -- Vacuum current_user's tables.
  -- Slow and therefore adequate punishment for going over quota.
  VACUUM FULL VERBOSE ANALYZE;

  -- Now re-instate privileges if enough space was reclaimed.
  SELECT total_disk_usage INTO disk_use
  FROM user_disk_usage
  WHERE rolname = session_user;
  IF (disk_use < <<your quota>>) THEN
    EXECUTE 'GRANT CREATE ON SCHEMA ' || <<user''s schema name>> || ', PUBLIC TO ' || user_data.rolname;
    -- GRANT INSERT privileges too, unless you work with BEFORE INSERT triggers on all tables
    RAISE NOTICE 'Disk use under quota limit. Privileges restored.';
  ELSE
    RAISE NOTICE 'Still using too much disk space. Free up more space.';
  END IF;
END; $$ LANGUAGE plpgsql;      

ロックアウトされたユーザーは、クォータ制限を下回るのに十分なデータを削除した後、この関数を自分で呼び出すことができます。

(全体のクォータではなく) ユーザーごとのクォータを一覧表示するテーブルを作成し、実際の使用量をそのクォータと比較したり、RAISE NOTICEクォータの 80% を超えたときに挿入トリガーを発行したりするなど、より高度な機能を追加できます (これには、すべてのテーブルで新しいテーブルの定期的なスイープで postgres ユーザーが簡単に実行できる before insert トリガーがあり、クォータを超えた場合は同じトリガーを使用して挿入を拒否できます)、その通知を 1 時間ごとに繰り返します (最後の通知がいつ発行されたかを記録します) )など

クォータはリアルタイムでチェックされないため、このソリューションは概算です。これは可能です (すべての挿入で user_quota_check() を実行し、session_user のテーブルのみをチェックするように変更します) が、興味を引くにはオーバーヘッドが多すぎる可能性があります。クォータを毎日管理するには、user_quota_check() を一晩実行します。そして、日中にあまりにも多くのスペースを使用しているユーザーを手動で非難します。

于 2014-03-15T11:51:33.260 に答える