电科金仓权限管理实战:那些让我加班到凌晨的权限“陷阱”

电科金仓权限管理实战:那些让我加班到凌晨的权限“陷阱”

上周五晚上10点,我正准备下班,开发组长小王火急火燎地跑过来:“哥,我们的报表系统崩了,用户说查不到数据!”我一看,又是权限问题——视图访问授权没做对。这已经是我们迁移到电科金仓后遇到的第N个权限问题了。今天,我把这些坑一个个挖出来,填平了给大家看。

开头:那个让我加班到凌晨的周五

说实话,我干了八年数据库运维,从Oracle到MySQL,啥场面没见过。但去年公司决定用电科金仓KES替换Oracle,我在权限管理上真是栽了不少跟头。

就说上周五那个事吧。我们的报表系统里有个核心视图,业务部门每天都要用。结果那天下午,突然所有用户都报“权限不足”。我查了半天,发现是有人误操作,把schema权限给改了。

这个经历让我意识到:金仓的权限体系很强大,但也很“较真”,一个细节没注意,整个系统都可能出问题。今天我就讲讲三个最常见的权限“陷阱”,都是我们团队用真金白银换来的经验。

陷阱一:视图访问授权——你以为授权了,其实没有

问题现场:“这个视图我能建,为啥别人不能查?”

我们有个数据分析师小张,他在自己的schema里建了个视图,然后想让其他部门的同事也能查。他觉得很简单:“不就是给个查询权限嘛!”结果操作完,同事还是报错。

我让他演示一下操作过程:

-- 小张(ud用户)建了个视图
\c - ud
CREATE VIEW ud.sales_view AS 
SELECT * FROM sales_data WHERE region = '华东';

-- 他觉得自己已经授权了
GRANT SELECT ON ud.sales_view TO report_user;

看起来没问题对吧?但report_user还是查不了。错误信息是:

ERROR: permission denied for schema ud

问题根源:schema权限是道“门”

我告诉小张:“你给了人家进房间的钥匙(视图权限),但没开门啊!”

在金仓里,schema就像一个个房间。你要进房间拿东西(查询视图),需要两把钥匙:

  1. 房间的门禁卡(schema的USAGE权限)
  2. 具体物品的取用权(视图的SELECT权限)

正确操作:两步授权法

我教小张正确的授权流程:

第一步:先开门(授权schema访问)

-- 需要schema的owner或者有授权权限的人来操作
\c - system  -- 用system用户,因为schema owner是system

-- 给report_user发门禁卡
GRANT USAGE ON SCHEMA ud TO report_user;

第二步:再授权物品(授权视图查询)

-- 小张可以自己操作,因为他是视图owner
\c - ud
GRANT SELECT ON ud.sales_view TO report_user;

这两步都做完,report_user才能正常查询。

进阶技巧:批量授权和角色管理

后来我们发现,这种需求很常见。与其每次都手动操作,不如建立一套权限管理体系。

方案一:创建只读角色

-- 创建一个专门用于查询的角色
CREATE ROLE report_viewer NOLOGIN;

-- 批量授权所有视图
GRANT SELECT ON ALL TABLES IN SCHEMA ud TO report_viewer;
-- 注意:这个ALL TABLES包括视图!

-- 把角色赋给用户
GRANT report_viewer TO report_user1, report_user2, report_user3;

方案二:设置默认权限
如果我们希望未来新建的视图也能自动授权,可以设置默认权限:

-- 设置默认权限,以后ud用户在ud schema下建的所有表/视图,
-- report_viewer角色都能查
ALTER DEFAULT PRIVILEGES FOR USER ud IN SCHEMA ud 
GRANT SELECT ON TABLES TO report_viewer;

这个功能特别有用,设好之后就不用每次手动授权了。

真实案例:我们搞砸过一次

有一次我们给一个新员工授权,忘了给schema权限。结果这位同事试了半天都查不了数据,还以为系统坏了,差点提了紧急工单。

后来我们做了个授权检查清单,每次授权都按这个来:

  1. 检查用户是否需要schema权限
  2. 检查用户是否需要具体对象权限
  3. 验证授权是否生效
  4. 通知用户测试

陷阱二:权限简写——那些神秘字母的含义

问题现场:“arwdDxt是啥?外星语吗?”

刚用金仓的时候,我最头疼的就是看权限信息。比如这样的:

ud=arwdDxt/ud
role01=r/ud

我问团队里的小李:“你能看懂这啥意思吗?”小李一脸懵:“王哥,这比摩斯密码还难懂啊!”

解密:每个字母都有含义

其实这是金仓的权限简写系统。我后来专门研究了文档,搞明白了每个字母的意思:

  • a - INSERT(追加)
  • r - SELECT(读取)
  • w - UPDATE(写入)
  • d - DELETE(删除)
  • D - TRUNCATE(清空)
  • x - REFERENCES(外键引用)
  • t - TRIGGER(触发器)

所以arwdDxt就是拥有所有权限,r就是只有查询权限。

实战:如何查看和分析权限

查看表权限:

-- 最简单的查法
\dp 表名

-- 或者用SQL查
SELECT 
    relname AS 表名,
    relacl AS 权限信息
FROM sys_class 
WHERE relname = 'sales_data';

查看schema权限:

SELECT 
    nspname AS schema,
    sys_catalog.sys_get_userbyid(nspowner) AS 所有者,
    nspacl AS 权限信息
FROM sys_namespace 
WHERE nspname = 'ud';

查看数据库权限:

SELECT 
    datname AS 数据库名,
    sys_catalog.sys_get_userbyid(datdba) AS 所有者,
    datacl AS 权限信息
FROM sys_database;

小技巧:写个解析函数

为了让团队都能看懂,我写了个权限解析函数:

CREATE OR REPLACE FUNCTION decode_privileges(priv_text TEXT)
RETURNS TABLE(privilege_name TEXT, privilege_desc TEXT) AS $$
DECLARE
    priv_mapping TEXT[][] := ARRAY[
        ['a', 'INSERT(插入数据)'],
        ['r', 'SELECT(查询数据)'],
        ['w', 'UPDATE(更新数据)'],
        ['d', 'DELETE(删除数据)'],
        ['D', 'TRUNCATE(清空表)'],
        ['x', 'REFERENCES(外键引用)'],
        ['t', 'TRIGGER(触发器操作)']
    ];
    i INT;
    ch CHAR;
BEGIN
    -- 遍历权限字符串的每个字符
    FOR i IN 1..length(priv_text) LOOP
        ch := substring(priv_text FROM i FOR 1);
        
        -- 查找对应的权限描述
        FOR j IN 1..array_length(priv_mapping, 1) LOOP
            IF priv_mapping[j][1] = ch THEN
                privilege_name := ch;
                privilege_desc := priv_mapping[j][2];
                RETURN NEXT;
                EXIT;
            END IF;
        END LOOP;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

-- 使用示例
SELECT * FROM decode_privileges('arwd');

现在团队里谁看不懂权限简写,就跑一下这个函数,一目了然。

经验教训:权限审计很重要

我们曾经遇到过权限泄露问题。有人给一个临时账户授予了过大权限,后来忘了回收。幸亏我们定期做权限审计:

-- 每月一次的权限审计脚本
SELECT 
    '高风险:过度授权' AS 问题类型,
    grantee::regrole AS 用户,
    table_schema || '.' || table_name AS 对象,
    privilege_type AS 权限
FROM information_schema.role_table_grants
WHERE grantee NOT IN ('system', 'sso', 'sao')  -- 排除管理员
  AND privilege_type IN ('DELETE', 'TRUNCATE', 'REFERENCES', 'TRIGGER')
  AND table_schema NOT LIKE 'pg_%'
  AND table_schema != 'information_schema'
ORDER BY grantee, table_schema, table_name;

陷阱三:VACUUM权限——普通用户的“禁区”

问题现场:“我连清理表都不行吗?”

这是我们运维小刘遇到的问题。他发现某个表膨胀得厉害,想手动VACUUM一下,结果:

\c - app_user
VACUUM sales_data;

-- 结果:
WARNING: skipping "sales_data" --- only table or database owner can vacuum it

小刘很郁闷:“我好歹也是这个表的owner,怎么连清理都不行?”

真相:VACUUM是个特权操作

我告诉小刘:“VACUUM在金仓里是个特殊操作,普通用户确实干不了。”

为什么呢?因为VACUUM涉及到底层存储结构的调整,有一定风险。金仓设计时,只允许两种人执行VACUUM:

  1. 表的owner
  2. 超级用户(superuser)

解决方案:几种变通方法

方法一:让表的owner来执行
这是最简单的办法。如果app_user是表的owner,理论上可以执行。但如果还是不行,可能是因为有其他限制。

方法二:临时提权(谨慎使用)

-- 用system用户临时授权
\c - system
GRANT system TO app_user;  -- 让app_user临时拥有system权限

-- app_user执行VACUUM
\c - app_user
VACUUM sales_data;

-- 用完马上回收
\c - system
REVOKE system FROM app_user;

方法三:设置超级用户(不推荐)

ALTER USER app_user WITH SUPERUSER;

但这个方法太危险了!一旦给用户超级权限,他就能为所欲为,包括删除整个数据库。

我们的实践:建立VACUUM管理流程

我们公司最后定了一套规矩:

  1. 普通用户不准手动VACUUM:全部交给自动VACUUM机制
  2. 紧急情况走流程:确实需要手动VACUUM时,提工单给DBA团队
  3. DBA统一操作:每周一早上,DBA检查表膨胀情况,统一执行VACUUM

我们还写了个监控脚本,自动发现需要VACUUM的表:

-- 监控表膨胀的脚本
CREATE OR REPLACE FUNCTION check_table_bloat()
RETURNS TABLE(
    schema_name TEXT,
    table_name TEXT,
    row_count BIGINT,
    dead_row_count BIGINT,
    bloat_ratio NUMERIC(5,2),
    need_vacuum BOOLEAN
) AS $$
BEGIN
    RETURN QUERY
    SELECT 
        schemaname::TEXT,
        tablename::TEXT,
        n_live_tup,
        n_dead_tup,
        CASE 
            WHEN n_live_tup = 0 THEN 0
            ELSE ROUND(n_dead_tup * 100.0 / n_live_tup, 2)
        END AS bloat_ratio,
        (n_dead_tup > 1000 AND n_dead_tup > n_live_tup * 0.2) AS need_vacuum
    FROM sys_stat_user_tables
    WHERE n_dead_tup > 0
    ORDER BY bloat_ratio DESC;
END;
$$ LANGUAGE plpgsql;

-- 每天检查一次
SELECT cron.schedule('check-bloat-daily', '0 2 * * *', 
    $$INSERT INTO vacuum_log 
    SELECT *, CURRENT_TIMESTAMP FROM check_table_bloat() 
    WHERE need_vacuum = true$$);

血的教训:一次VACUUM事故

去年我们有个新来的DBA,想提高性能,给一个业务用户临时加了超级权限执行VACUUM。结果完事后忘了回收权限。

一个月后,这个用户误操作删除了一个重要表。虽然最后从备份恢复了,但业务停了两个小时,损失不小。

从此以后,我们立下铁规:绝不给人超级权限,VACUUM必须走流程

权限管理最佳实践:我们总结的“三要三不要”

通过这三年的实践,我们总结了金仓权限管理的“三要三不要”:

三要

1. 要分层授权

-- 不好的做法:一次性给所有权限
GRANT ALL ON SCHEMA sales TO sales_user;

-- 好的做法:分层授权
GRANT USAGE ON SCHEMA sales TO sales_user;  -- 基础权限
GRANT SELECT ON sales.customer TO sales_user;  -- 具体表权限
GRANT INSERT, UPDATE ON sales.order TO sales_user;  -- 特定操作的权限

2. 要用角色管理
不要直接给用户授权,而是通过角色:

-- 创建角色
CREATE ROLE sales_read_only;
CREATE ROLE sales_read_write;

-- 给角色授权
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO sales_read_only;
GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA sales TO sales_read_write;

-- 把角色给用户
GRANT sales_read_only TO user1;
GRANT sales_read_write TO user2;

3. 要定期审计
每月做一次权限审计:

-- 审计脚本
CREATE TABLE permission_audit_log (
    audit_id SERIAL PRIMARY KEY,
    audit_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    object_type TEXT,
    object_name TEXT,
    grantee TEXT,
    privileges TEXT,
    auditor TEXT
);

-- 每月1号自动运行
SELECT cron.schedule('permission-audit', '0 0 1 * *', $$
    INSERT INTO permission_audit_log 
    (object_type, object_name, grantee, privileges, auditor)
    SELECT 
        'TABLE',
        schemaname || '.' || tablename,
        grantee,
        privilege_type,
        CURRENT_USER
    FROM information_schema.role_table_grants
    WHERE grantee NOT IN ('system', 'sso', 'sao');
$$);

三不要

1. 不要给人超级权限

-- 千万别这么干!
ALTER USER app_user WITH SUPERUSER;

2. 不要用ALL PRIVILEGES

-- 太危险了!
GRANT ALL PRIVILEGES ON DATABASE app_db TO app_user;

-- 应该细化授权
GRANT CONNECT ON DATABASE app_db TO app_user;
GRANT CREATE ON SCHEMA app_schema TO app_admin;

3. 不要忘记回收权限

-- 员工离职时一定要做
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA sales FROM former_employee;
REVOKE ALL PRIVILEGES ON SCHEMA sales FROM former_employee;
REVOKE CONNECT ON DATABASE app_db FROM former_employee;
DROP USER former_employee;

写在最后:权限管理是门学问

从Oracle转到电科金仓,我在权限管理上确实花了些时间适应。但说实话,适应之后发现金仓的权限体系设计得挺合理的——严格但清晰,复杂但可控。

现在回想那些加班到凌晨的日子,虽然辛苦,但值得。因为我们建立了一套安全、可控、易管理的权限体系。

如果你也在用金仓,或者在考虑用金仓,我建议你:

  1. 一定要理解三权分立:system、sso、sao各司其职
  2. 一定要从简单开始:先给最小权限,不够再加
  3. 一定要有文档:每个权限变动都要记录
  4. 一定要定期审查:权限就像房间,久了不打扫会乱

最后,如果你在权限管理上遇到了问题,或者有更好的实践经验,欢迎到金仓技术探索社区(https://kingbase.com.cn/explore)交流。那里有很多实战案例和技术文章,说不定能帮你少走弯路。

权限管理这条路,我们走过来了。虽然踩过坑、加过班,但最终建成了一套让业务安心、让运维省心的权限体系。希望我们的经验,能帮你走得更稳、更快。

posted @ 2026-01-14 17:28  性感的猴子  阅读(0)  评论(0)    收藏  举报  来源