靠谱,但仅限轻量级逻辑隔离;它依赖GRANT限制库表访问,不防误操作、SQL注入或越权,且无法隐藏SHOW DATABASES中的其他库名。

MySQL 多租户下,用用户权限做逻辑隔离靠谱吗?
靠谱,但仅限于“轻量级逻辑隔离”——它不防误操作,更不防 SQL 注入或应用层越权。本质是靠 GRANT 限制用户只能访问指定数据库(或表),不涉及数据行级、连接池隔离或元数据隐藏。
适用场景:SaaS 后端用单实例托管多个小客户,每个客户独占一个库,且信任应用层不做跨库查询;不适合敏感数据混跑或需强审计的场景。
- 必须为每个租户创建独立 MySQL 用户,不能复用同一账号
- 每个租户对应唯一数据库名(如
tenant_abc),建库后立刻REVOKE所有全局权限 -
GRANT SELECT, INSERT, UPDATE, DELETE ON tenant_abc.* TO 'tenant_abc_user'@'%'—— 注意末尾的.*,漏掉会没表权限 - 禁止授予
USAGE以外的任何全局权限(比如PROCESS、SHOW DATABASES),否则用户能看见其他库名
为什么 SHOW DATABASES 还能看到其他租户库?
这是 MySQL 默认行为:只要用户存在,哪怕没任何权限,也能在 SHOW DATABASES 中看到所有库名(除非启用 skip-show-databases 或升级到 8.0+ 并配 information_schema_restricted)。
根本原因不是权限没设好,而是 MySQL 的权限模型里,“看见库名”和“能访问库”是两回事。租户用户执行 USE other_tenant_db 会直接报错 ERROR 1044 (42000): Access denied for user...,但列表本身拦不住。
- MySQL 5.7 及更早:无法隐藏库名,只能靠应用层不暴露
SHOW DATABASES结果 - MySQL 8.0.12+:可设置
information_schema_restricted=ON,再配合GRANT SELECT ON information_schema.*细粒度控制,但依然不能完全屏蔽库名列表 - 真正有效的方式是代理层过滤(如 ProxySQL)或应用连接时固定指定
database参数,不依赖用户自己查
CREATE DATABASE 权限能不能放开给租户用户?
不能。一旦授予 CREATE DATABASE,用户就能建任意库,包括命名冲突、绕过租户前缀、甚至故意建 mysql 同名库导致异常。
租户库必须由运维或管理服务统一创建,且命名强制规范(如 tenant_{id}),创建后立即绑定专属用户并回收 CREATE 权限。
- 建库脚本中必须显式指定
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,避免租户自己建表时继承 server 默认导致排序差异 - 不要用
GRANT ALL ON *.*临时授权再回收——权限缓存可能延迟,且ALL包含GRANT OPTION,风险极大 - 检查是否残留
GRANT ... WITH GRANT OPTION:执行SELECT * FROM mysql.user WHERE User = 'tenant_xxx';看Grant_priv字段
应用连接时没指定 database 会怎样?
连接成功,但执行任何表操作都会报错 ERROR 1046 (3D000): No database selected。这不是权限问题,是 MySQL 协议要求明确上下文。
很多 ORM(如 Django、Laravel)默认不带 database 参数连,或只在配置里写 host/port/user/pass,漏掉 db 名,上线就炸。
- PHP PDO 连接字符串必须含
;dbname=tenant_xyz - Java JDBC URL 要写全
jdbc:mysql://host:3306/tenant_xyz?useSSL=false - Node.js mysql2 配置里
database: 'tenant_xyz'是必填项,不能留空或设为null - 连接池初始化阶段就要验证:执行一次
SELECT DATABASE(),确保返回值等于预期租户库名
租户隔离最脆弱的一环从来不是权限配置,而是应用连接串写死、迁移脚本绕过租户前缀、或者 DBA 临时给某个用户加了 SHOW VIEW 权限结果泄露了其他库的视图定义。每次变更都要当它是生产事故来验。


















