唯一真正隔离的方案是为每个业务系统建独立数据库、独立账号并仅授本库必要权限;须成对执行create database与create user,绑定库与账号,禁用通配符授权和'%'主机,显式grant并flush privileges,跨库访问须通过api或本库视图,严禁with grant option与全局权限。

直接为每个业务系统建独立数据库 + 独立账号 + 仅授本库必要权限,是唯一能真正隔离的方案。用前缀表+全库授权,等于没隔离。
CREATE DATABASE 和 CREATE USER 必须成对执行
模块库和账号必须一一绑定,不能先建库再“统一授权”。常见错误是建完 db_order、db_user 后,给一个账号授 ON *.* 或 ON `db_%`.* —— 这会让权限跨域失效。
- 建库时指定字符集:
CREATE DATABASE db_order CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 建账号必须带具体 host,禁用
'%'(除非走跳板机):CREATE USER 'app_order'@'10.20.30.%' IDENTIFIED BY 'strong-pass-2026'; - 授权只写明库名,不加通配符:
GRANT SELECT, INSERT, UPDATE ON db_order.* TO 'app_order'@'10.20.30.%'; - 立刻刷新:
FLUSH PRIVILEGES;(MySQL 5.7+ 部分操作自动刷新,但显式执行更稳妥)
跨库读取必须绕过 GRANT,不能直连授 SELECT ON other_db.*
订单服务要查用户昵称,不代表它该有 db_user 的任何权限。一旦授了,就等于把整个用户库暴露给订单模块。
- 优先走 API:由用户服务提供
/api/v1/users/{id}/basic,订单服务调用 HTTP - 次选只读视图(仅限低延迟强耦合场景):
CREATE VIEW order_user_info AS SELECT id, nickname FROM db_user.users; - 视图必须在本库(
db_order)中建,并显式授权:GRANT SELECT ON db_order.order_user_info TO 'app_order'@'10.20.30.%'; - 严禁
DEFINER = 'root'@'%':否则低权限账号可借 root 身份执行任意逻辑
权限验证不能只看 GRANT 语句,要登录后实测
很多人执行完 GRANT 就以为完事了,结果应用连不上或报错 Access denied for user,往往卡在细节上。
- 用对应账号登录:
mysql -u app_order -p -h 10.20.30.5,确认能连 - 执行
SHOW DATABASES;:应只看到db_order(其他库名会显示但无法访问,这是正常行为) - 切换库:
USE db_order;成功,USE db_user;应报错Access denied - 查表:
SELECT * FROM users LIMIT 1;成功;DROP TABLE users;应报错ERROR 1142 (42000): DROP command denied
生产环境必须禁用 WITH GRANT OPTION 和全局权限
哪怕只是临时加个 WITH GRANT OPTION,都可能被误用来创建子账号、越权建库。这不是理论风险,是真实发生过的线上事故。
- 检查高危权限:
SELECT user,host,Grant_priv,Drop_priv FROM mysql.user WHERE Grant_priv='Y' OR Drop_priv='Y'; - 回收误授:
REVOKE GRANT OPTION ON *.* FROM 'app_order'@'10.20.30.%'; - 禁止 ALL PRIVILEGES:
GRANT SELECT,INSERT,UPDATE,DELETE显式列出,不写ALL - 定期清理:
DROP USER IF EXISTS 'old_app'@'10.20.%.%';(离职/下线模块后立即执行)
最容易被忽略的是 host 绑定和 FLUSH PRIVILEGES 的组合效果——改了 mysql.user 表但没刷内存,或 host 写成 '%' 却以为只限内网,这两点导致的越权问题占线上权限事故的七成以上。











