能,MySQL通过GRANT语句将权限精确限制在指定database(如tenant_001.),禁用ON .*和WITH GRANT OPTION,并配合应用层校验database参数与账号权限匹配,可实现可靠租户隔离。

MySQL多租户用多个Database隔离,权限能真正隔开吗?
能,但前提是权限粒度控制到database级别,并且不给跨库操作权限。MySQL本身没有“租户”概念,靠GRANT语句限制用户只能访问指定database,这是最常用也最可靠的隔离方式。
常见错误是只创建了不同database,却给用户分配了ALL PRIVILEGES ON *.*或USAGE以外的全局权限——这样租户A执行USE tenant_b_db或直接SELECT * FROM tenant_b_db.users就能越权访问。
- 必须用
GRANT ... ON `tenant_a_db`.* TO 'user_a'@'%',而不是ON *.* - 避免使用
WITH GRANT OPTION,否则租户可自行授权给他人 - 建库时统一加前缀(如
tenant_123_users),方便后续用LIKE批量管理权限
如何安全创建租户专用Database和用户?
关键不是“建库建用户”,而是建完立刻绑定权限、禁用跨库能力。MySQL不会自动阻止用户连接后切换数据库,全靠GRANT语句显式限定作用域。
示例流程:
CREATE DATABASE `tenant_001`; CREATE USER 'tenant_001_app'@'%' IDENTIFIED BY 'strong-pass-2024'; GRANT SELECT, INSERT, UPDATE, DELETE ON `tenant_001`.* TO 'tenant_001_app'@'%'; FLUSH PRIVILEGES;
- 不要省略
FLUSH PRIVILEGES,尤其在直接操作mysql.user表后 - 用户host尽量写具体IP或子网(如
'tenant_001_app'@'10.20.30.%'),不用'%' - 避免用
root或共享DBA账号执行租户DDL,应由自动化脚本或专用权限初始化服务完成
为什么SHOW DATABASES还能看到其他租户库?
这是MySQL默认行为:只要用户有任意库权限,SHOW DATABASES就会列出所有库名(除非开启show_database_privilege限制,但该变量5.7+已移除)。这不是权限泄漏,只是元信息可见。
实际影响有限,但对敏感场景仍是隐患:
- 租户应用若未限定
database连接参数,可能误连错库(比如配置里写了database=tenant_002却连了tenant_001的账号) -
INFORMATION_SCHEMA中SCHEMATA表仍可查到库名,但无表结构/数据权限 - 解决办法:应用层强制校验连接时
database参数与账号权限匹配;或用ProxySQL等中间件过滤SHOW DATABASES结果
跨租户查询失败但没报错?检查sql_mode和默认Database
当应用执行SELECT id FROM users却意外读到其他租户数据,大概率是因为连接时没指定database,又碰上sql_mode含IGNORE_SPACE或用户有多个库权限——MySQL会按FROM子句里有没有库名前缀来决定查哪张表。
典型陷阱:
- 连接字符串漏写
database=tenant_001,导致USE未生效,后续无库前缀的SQL走默认库(可能是information_schema或空上下文) - 开发本地测试时用
root账号,权限太宽,上线后换成受限账号才发现SQL报ERROR 1146 (42S02): Table 'users' doesn't exist - ORM框架(如Laravel Eloquent)若配置了
prefix但没设database,生成的SQL不含库名,依赖连接时的默认库
最稳妥的做法:所有SQL显式带上库名前缀,例如SELECT * FROM `tenant_001`.users;连接池初始化时强制USE tenant_001。


















