最稳方案是数据库连接层硬隔离:PostgreSQL 用 pg_hba.conf 限制 IP+用户+库+SSL+只读,MySQL 用 CREATE USER host 字段与网络防火墙双校验,并关闭 BI 工具自动 DDL 功能。
PostgreSQL 用 pg_hba.conf 控制只读用户 + IP 白名单最稳
直接给结论:别靠应用层“约定俗成”的只读权限,必须在数据库连接层做硬隔离。postgresql 的 pg_hba.conf 是唯一能同时约束「谁(ip)」、「用哪个用户」、「连哪个库」、「走什么协议」、「是否只读」的配置点。
常见错误是只建了个只读角色(CREATE ROLE reader WITH NOSUPERUSER NOCREATEDB NOCREATEROLE;),但没在 pg_hba.conf 里限制该用户只能从 BI 工具固定出口 IP 连入——结果这个账号一旦泄露或被复用,就等于裸奔。
-
pg_hba.conf每条规则按顺序匹配,把 BI 的白名单规则放在所有host规则最前面,避免被更宽泛的0.0.0.0/0覆盖 - 只允许
hostssl(强制 SSL),BI 工具基本都支持,不加这层,密码和查询内容都在明文传输 - 对应用户的
pg_database级权限要显式REVOKE CONNECT ON DATABASE xxx FROM PUBLIC;,再GRANT CONNECT给该用户,防止意外连上其他库
MySQL 8.0+ 用 CREATE USER + host 字段做双保险
MySQL 的 host 字段不是摆设,它和网络层的 IP 白名单形成嵌套校验:先过 TCP 层防火墙(如云厂商安全组),再过 MySQL 的 host 匹配。两者缺一不可。
典型翻车现场:BI 工具部署在 Kubernetes 集群里,出口 IP 是 NAT 网关地址,但 DBA 在 CREATE USER 'bi_reader'@'10.20.30.%' 里写了 Pod 网段——永远连不上。
- 查清楚 BI 工具实际发起连接的源 IP:
tcpdump -i any port 3306 -nn -A | grep "User:"抓包看,比文档更准 - MySQL 8.0+ 支持
CREATE USER 'bi_reader'@'203.208.60.123' IDENTIFIED BY 'xxx' REQUIRE SSL;,host必须写死 IP 或带通配符的 CIDR(如203.208.60.0/24),不能写域名(DNS 解析发生在认证后) - 只读权限别只
GRANT SELECT,还要REVOKE DROP, ALTER, INSERT, UPDATE, DELETE, CREATE, INDEX, REFERENCES—— MySQL 的权限模型里,SELECT不自动排斥其他操作
BI 工具侧也要关掉「自动建表/改结构」开关
很多 BI 工具(比如 Tableau、Superset、Power BI)默认会尝试 CREATE TEMP TABLE 或 ANALYZE,哪怕你只拖了几个字段,它也可能发 DDL。数据库只给了 SELECT 权限,就会报错,但错误信息常被包装成「查询超时」或「连接失败」,误导排查方向。
- Tableau Desktop:设置 → 数据源 → 右键 → 「编辑数据源」→ 「数据源属性」→ 取消勾选「在初始查询中包括统计信息」
- Superset:数据集 → 编辑 → 「高级」选项卡 → 关闭
Allow DML和Fetch values for filters(后者会触发SELECT DISTINCT,某些视图下也报错) - Power BI Desktop:文件 → 选项 → 数据源 → 找到对应连接 → 点击「清除凭据」再重连,并确保「隐私级别」设为「组织」而非「公共」,否则它可能绕过你的只读约束去试探元数据
验证是否真只读:用 EXPLAIN ANALYZE 看执行计划里有没有写操作
最可靠的检验方式,不是看用户有没有 INSERT 权限,而是抓一次 BI 发出的真实 SQL,用数据库管理员账号跑 EXPLAIN ANALYZE,看执行计划里是否出现 ModifyTable、LockRows、Result(含 CTE 写临时表)这类节点。
容易忽略的一点:某些 BI 工具在「刷新缓存」或「预计算度量」时,会悄悄发 CREATE MATERIALIZED VIEW AS ... 或 INSERT INTO ... SELECT,而这些语句不会出现在你手动写的仪表板 SQL 里。
- 在数据库侧开
log_statement = 'all'(仅临时),过滤出 BI 用户的连接日志,确认每条语句都是SELECT或SHOW - 对关键视图加
SECURITY DEFINER函数封装,内部用SET LOCAL statement_timeout = 5000;防止慢查询拖垮实例 - 如果 BI 工具必须用
TEMP表(比如做多维分析中间结果),那就单独建一个轻量级只读 schema,授权CREATE给该用户,但禁止其对主业务 schema 的任何 DDL 权限

















