生产环境MySQL应遵循最小权限原则:按业务模块分账户、限定IP、禁用root远程、用角色管理权限、启用审计日志并定期审查。

创建用户时只赋予最小必要权限
MySQL 默认的 GRANT ALL PRIVILEGES 会把所有库、所有表、所有操作权限都给用户,生产环境绝对不能这么干。应该按业务模块拆分账户,比如订单服务只配 order_db 的 SELECT, INSERT, UPDATE,且仅限于 orders 和 order_items 表。
实操建议:
- 用
CREATE USER 'app_order'@'10.20.30.%' IDENTIFIED BY 'strong_pwd_2024';明确限定 IP 段,避免通配符%开放全网 - 授权时用
GRANT SELECT, INSERT, UPDATE ON order_db.orders TO 'app_order'@'10.20.30.%';,不加WITH GRANT OPTION - 禁止对
mysql系统库授任何非 DBA 权限,否则可能绕过权限校验
定期审查账号与权限变更日志
MySQL 8.0+ 默认不开启权限变更审计,information_schema.role_table_grants 和 mysql.user 只反映当前状态,无法追溯“谁在什么时候加了什么权限”。没有审计就等于无法追责。
实操建议:
- 启用 MySQL 企业版的 Audit Log 插件,或社区版配合
general_log(仅调试用,性能损耗大) - 写脚本定期导出
SELECT user, host, account_locked, password_last_changed FROM mysql.user;和SELECT * FROM information_schema.role_table_grants;做基线比对 - 对
DROP USER、GRANT、REVOKE操作,在运维平台中强制走审批流,留痕到外部系统
用角色(ROLE)管理权限组而非直授用户
MySQL 8.0 引入 ROLE 后,直接给用户授一堆权限的做法已经过时。角色能解耦权限定义与用户绑定,改权限只需动角色,不用逐个用户 REVOKE/GRANT。
实操建议:
- 先建角色:
CREATE ROLE 'role_rpt_reader'; - 批量授权:
GRANT SELECT ON sales_db.* TO 'role_rpt_reader'; - 再把角色赋给用户:
GRANT 'role_rpt_reader' TO 'bi_user'@'%'; - 启用角色需执行
SET DEFAULT ROLE 'role_rpt_reader' TO 'bi_user'@'%';,否则登录后权限不生效
禁用 root 远程登录并限制超级权限使用场景
root 账户拥有 SUPER、SHUTDOWN、REPLICATION CLIENT 等高危权限,一旦泄露或被误用,可导致主从断裂、配置篡改甚至删库。线上数据库必须剥离其远程访问能力。
实操建议:
- 执行
DELETE FROM mysql.user WHERE user = 'root' AND host != 'localhost'; FLUSH PRIVILEGES;清除所有非本地 root 登录点 - DBA 日常维护用专用低权账号(如
dba_maint),只在需要时临时GRANT PROCESS, SHOW VIEW ON *.* TO 'dba_maint'@'10.20.30.%'; - 禁止应用代码里出现
user=root配置项,CI/CD 流水线中加入 SQL 检查规则,匹配user\s*=\s*['"]root['"]并阻断发布
SELECT u.User, u.Host, u.Account_locked, GROUP_CONCAT(DISTINCT r.Role) AS Roles, GROUP_CONCAT(DISTINCT CONCAT(p.Table_schema, '.', p.Table_name, ':', p.Privilege)) AS DirectPrivs FROM mysql.user u LEFT JOIN mysql.role_edges re ON u.User = re.Granted_role AND u.Host = '%' LEFT JOIN mysql.roles_mapping r ON re.Granted_role = r.Role LEFT JOIN information_schema.role_table_grants p ON r.Role = p.Grantee GROUP BY u.User, u.Host;
权限模型越细,维护成本越高;但比起一次越权导致的数据泄漏或误删,多写几行 GRANT 和定期跑一遍检查脚本,其实更省事。真正难的是让开发和运维都接受“权限不是默认有,而是申请才有”这个前提。










