我有一个问题,到目前为止还没有找到任何线索。我会尽力解释它,但请随时询问更多细节!
上下文
我在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表空间。
问题
可以将用户限制为仅在其拥有的表空间中创建对象吗?
答案 0 :(得分: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
回来。然而,这可能导致对用户采取更激烈的行动。优选地,您将在数据库中的每个表上创建一个before 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;
锁定的用户在删除了足够的数据以达到配额限制后,可以自行调用此功能。
您可以添加更复杂的功能,例如列出每个用户的配额(而不是总配额),并将实际使用情况与该配额进行比较,当超过80%时,在插入触发器上发出RAISE NOTICE
of quota(这要求每个表都有一个before insert触发器,postgres用户可以在新表的常规扫描中轻松完成,如果超过配额,可以使用相同的触发器拒绝插入),每小时重复一次通知(所以在发出最后通知时记录),等等。
此解决方案是近似值,因为未实时检查配额。这是可能的(在每个插入上运行user_quota_check(),修改为仅检查session_user的表)但最有可能过多的开销使它变得有趣。隔夜运行user_quota_check()以进行配额的日常管理。并且在白天手动鞭打任何使用太多空间的用户。