创建专用账户必须指定确切host而非%通配,如'10.20.30.40'或内网域名;授权需最小化,按需赋予SELECT/INSERT/UPDATE/DELETE等具体权限;MySQL 8.0+应指定mysql_native_password插件并刷新连接池。

创建专用账户时必须指定 host 而非用 % 通配
很多应用连接失败,根源在于创建用户时用了 CREATE USER 'appuser'@'%' IDENTIFIED BY 'pwd',但实际应用服务器 IP 是固定的,MySQL 默认拒绝从非匹配 host 的连接。更安全也更可靠的做法是明确指定应用服务器的 IP 或内网域名:
CREATE USER 'appuser'@'10.20.30.40' IDENTIFIED BY 'strong_pwd_2024'- 若应用部署在容器或 K8s 内,host 可能是
'app-service.default.svc.cluster.local',需确认 DNS 可解析 - 避免使用
'%',除非你明确做了防火墙或 proxy 层限制,否则等于开放外网可连
授权要遵循最小权限原则,慎用 GRANT ALL
给应用账户赋权不是越全越好,GRANT ALL ON mydb.* TO 'appuser'@'10.20.30.40' 很可能赋予了 DROP DATABASE、CREATE USER 等完全不需要的能力。应按应用真实 SQL 行为拆分:
- 只读服务:用
GRANT SELECT ON mydb.users TO 'appuser'@'10.20.30.40',甚至限定到具体字段(需配合视图) - 增删改服务:通常只需
SELECT, INSERT, UPDATE, DELETE,显式列出比ALL PRIVILEGES更可控 - 避免跨库授权,如
mydb1.*和mydb2.*应分开授权,防止误操作波及其他业务
刷新权限后仍连不上?检查 sql_mode 和 default_authentication_plugin
MySQL 8.0+ 默认认证插件变为 caching_sha2_password,而老版本客户端(如某些 Python MySQLdb、旧版 PHP mysqli)不支持,会报错 Client does not support authentication protocol requested by server。解决方式不是降级服务端,而是适配账户:
- 建用户时强制指定插件:
CREATE USER 'appuser'@'10.20.30.40' IDENTIFIED WITH mysql_native_password BY 'pwd' - 或对已有用户修改:
ALTER USER 'appuser'@'10.20.30.40' IDENTIFIED WITH mysql_native_password BY 'pwd' - 同时确认应用连接串里没硬编码
sql_mode=STRICT_TRANS_TABLES类参数,某些 ORM(如旧版 Django)默认行为可能与服务端 strict mode 冲突
权限变更后记得 FLUSH PRIVILEGES,但注意它不总生效
FLUSH PRIVILEGES 是重载权限表的命令,但它只对 mysql.user 等系统表的直接修改有效。如果你用的是 GRANT 语句,权限已即时写入内存,无需再 flush —— 多次执行反而可能掩盖真正问题。
- 真正需要
FLUSH PRIVILEGES的场景:手动INSERT INTO mysql.user后、或通过配置文件修改了skip-grant-tables恢复后 - 权限不生效?优先查
SHOW GRANTS FOR 'appuser'@'10.20.30.40'确认当前生效权限,再看SELECT user,host,plugin FROM mysql.user WHERE user='appuser'验证认证方式 - 应用连接池(如 HikariCP、Druid)常缓存连接,权限改完后需重启应用或清空连接池,否则旧连接仍用旧权限上下文
实际中最容易被忽略的是 host 解析精度和连接池缓存——IP 写错一位、DNS 缓存未刷新、连接复用旧会话,都会让权限设置看起来“没生效”。










