如果你使用磁盘配额,那么你给自己做了大量的工作。事实上,在 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 用户在定期扫描新表时轻松完成,如果超过配额,可以使用相同的触发器来拒绝插入),每小时重复一次通知(所以记录最后一次通知发出的时间), ETC。
此解决方案是近似的,因为未实时检查配额。这是可能的(在每次插入时运行 user_quota_check(),修改为仅检查 session_user 的表),但很可能开销太大而使其变得有趣。通宵运行 user_quota_check() 以进行配额的日常管理。并在白天手动鞭打任何使用过多空间的用户。