连接池账号必须仅具连接和健康检查权限,禁用业务操作权限。应使用USAGE权限配合SELECT mysql.health_check视图,显式指定host而非%以避免匹配失败,认证插件需与客户端兼容,且须禁用init-sql等潜在提权行为。

连接池账号不能有业务权限,只留连接和健康检查能力
应用连接池账号的唯一职责是维持连接、响应心跳探测,它不该具备任何数据操作能力。一旦赋予SELECT以外的权限(比如INSERT或EXECUTE),就等于把业务逻辑的执行权交给了连接层,违背解耦原则。
正确做法是让这个账号只能登录、能查一个轻量健康检查视图(如mysql.health_check),其余全拒。MySQL 的 USAGE 权限就是干这事的:它不授任何实际权限,仅允许建立连接。
CREATE USER 'pool_user'@'10.20.30.%' IDENTIFIED WITH mysql_native_password BY 'strong_pwd_2026';GRANT USAGE ON *.* TO 'pool_user'@'10.20.30.%';-
GRANT SELECT ON mysql.health_check TO 'pool_user'@'10.20.30.%';(需提前建好该视图) - 绝对不要给
SUPER、PROCESS、REPLICATION CLIENT——这些是监控或DBA用的,不是连接池需要的
为什么必须显式指定 host 而非用 %
用 'pool_user'@'%' 看似方便,但实际会导致连接被拒绝,尤其在容器或云环境里。MySQL 严格匹配 host 字段,而应用真实出口 IP 往往不是 % 能覆盖的范围。
常见失败场景:应用部署在 Kubernetes Pod 中,IP 是 172.18.0.5,但你建的是 @'%';结果 MySQL 认为该连接来自未授权 host,直接拒绝,报错 Access denied for user 'pool_user'@'172.18.0.5'。
- 查清应用服务器真实出站 IP(不是容器内网、不是负载均衡私有地址)
- 用具体网段,例如
'pool_user'@'10.20.30.%'或精确 IP'pool_user'@'10.20.30.40' - 如果走 ProxySQL/Vitess,host 应设为代理机器 IP,如
'pool_user'@'10.100.5.10' - 别信 DNS 主机名——DNS 解析失败时,MySQL 不会 fallback 到
%
认证插件不兼容会导致“权限错误”假象
MySQL 8.0 默认用 caching_sha2_password,但很多连接池(如 HikariCP 旧版、Druid 1.2.x)或 ORM 初始化器不支持该插件,连接时卡在 Access denied,你以为是权限问题,其实是握手失败。
验证方式很简单:用命令行模拟连接:mysql -u pool_user -p -h db-prod.internal --default-auth=mysql_native_password。如果成功,说明问题出在客户端认证能力上。
- 建号时必须显式指定插件:
IDENTIFIED WITH mysql_native_password BY 'pwd' - 已有账号可修复:
ALTER USER 'pool_user'@'10.20.30.%' IDENTIFIED WITH mysql_native_password BY 'pwd'; - 确认应用连接字符串里没带
serverTimezone之类干扰参数,避免触发额外认证协商
连接池初始化脚本可能偷偷提权
最隐蔽的风险不是账号本身权限大,而是连接池启动时执行的初始化 SQL(比如 connection-init-sql)用了高权限上下文。有些 ORM 迁移工具(如 Flyway、Liquibase)会在首次连接时自动执行 DDL,若此时用的是 pool_user,而它又被误授了 CREATE 权限,就会绕过所有设计。
真实案例:应用配置了 init-sql=SET SESSION sql_mode='STRICT_TRANS_TABLES',看似无害,但若 init-sql 被替换成 DROP TABLE users,而账号又有 DROP 权限——删库就在毫秒间。
- 禁用所有连接池的
init-sql功能,或确保其内容只含安全会话变量设置 - 在数据库侧加审计规则:对
pool_user账号限制只允许执行SELECT和SET类语句(通过 ProxySQL 规则或 MySQL Enterprise Firewall) - 上线前手动用该账号连上去跑一遍:
DROP TABLE test;、SHOW CREATE TABLE users;,确认明确报错
真正难的不是建个低权限账号,而是确保整个链路——从连接建立、心跳探测、到任意初始化行为——都运行在同一个最小权限上下文中。任何一环松动,都会让前面所有配置失效。


















