应精确授予select或insert等权限至具体表并限定内网ip段,禁用'%'和localhost混用,mysql 8.0+须用角色封装权限,grant后无需flush privileges。

直接给业务账号授 SELECT 或 INSERT 到具体表,同时限定 host 为内网 IP 段,是唯一靠谱的起点。别信“先建用户再慢慢加权限”的说法——新建用户默认连 SELECT 都不能执行,但权限错配比没权限更危险。
GRANT 语句必须精确到 database.table 级别
常见错误是写成 GRANT SELECT ON mydb.* TO 'app'@'10.20.%',它会把日志表、配置表、审计表全放开。真实业务只读订单页?那就只给 orders 表:
GRANT SELECT ON shop_db.orders TO 'reporter'@'10.20.30.%'- 写操作账号也别贪全:
GRANT INSERT, UPDATE ON shop_db.orders TO 'order_writer'@'10.20.30.%',不带DELETE就别加 - 敏感字段单独控制:比如只允许查
orders表的id和status列:GRANT SELECT(id, status) ON shop_db.orders TO 'reporter'@'10.20.30.%'
host 必须具体,禁用 '%' 和 'localhost' 混用
用 'user'@'%' 等于放弃网络层防护。生产环境必须按部署位置指定 host:
- 应用服务器固定内网 IP:
'app_user'@'10.1.2.3' - K8s 场景可用 CIDR:
'app_user'@'10.100.0.0/255.255.0.0' - 开发网段限制:
'dev_user'@'192.168.5.%',不开放公网 - 绝对不要让
'user'@'localhost'和'user'@'127.0.0.1'同时存在——MySQL 把它们当两个账号,权限要分别管理
MySQL 8.0+ 必须用角色封装权限,且设默认角色
直接给用户授予权限难维护,尤其多个服务共用一类权限时。角色不是可选项:
- 建角色:
CREATE ROLE 'app_reader', 'app_writer' - 授角色权限:
GRANT SELECT ON shop_db.orders TO 'app_reader';GRANT INSERT, UPDATE ON shop_db.orders TO 'app_writer' - 绑用户并设默认角色:
GRANT 'app_reader' TO 'webapp'@'10.20.30.%'; SET DEFAULT ROLE 'app_reader' TO 'webapp'@'10.20.30.%' - 注意:
SHOW GRANTS FOR 'webapp'@'10.20.30.%'看不到角色权限,得用SHOW GRANTS FOR 'webapp'@'10.20.30.%' USING 'app_reader'
FLUSH PRIVILEGES 不仅多余,还可能掩盖问题
MySQL 5.7.6+ 和所有 8.0 版本中,GRANT 语句本身就会立即刷新权限缓存。执行 FLUSH PRIVILEGES 是典型误区:
- 常见错误现象:命令没报错,但新用户连不上——八成是没设密码或
host匹配失败 - 检查方式:
SELECT host,user,authentication_string FROM mysql.user WHERE user='xxx' - 只有在你直接修改了
mysql.user表(比如用UPDATE改密码)后,才需要FLUSH PRIVILEGES - 权限变更对已存在的连接不生效,应用侧需重启连接池或等连接自然释放
最易被忽略的一点:权限回收后,旧连接仍维持原权限,直到重连。别只盯着 SQL 是否执行成功,得确认应用是否真的用上了新权限配置。











