mysql 8.0+ 不支持用户级磁盘配额,仅能通过 os 层目录配额(如 setquota)结合独立表空间(data directory)和资源组限速写入间接管控磁盘使用。

MySQL 8.0+ 能否直接限制用户磁盘配额
不能。MySQL 本身不提供类似 QUOTA 或 MAX_DISK_USAGE 的用户级磁盘配额机制。你查到的 CREATE USER ... WITH MAX_QUERIES_PER_HOUR 等限制,只管 CPU/连接/查询频次,和磁盘空间完全无关。
真正能间接控制磁盘增长的,只有两个落点:表空间物理路径的 OS 层管控,以及资源组(Resource Groups)对写入行为的节流——但后者不直接限制“用了多少 GB”,而是压制“写得多快”。
用资源组限速写入来变相控盘
资源组适用于 MySQL 8.0.2+,核心思路是把高写入负载的线程绑定到低优先级资源组,拖慢 INSERT/UPDATE/REPLACE 速度,从而降低单位时间产生的 binlog、redo log、临时文件和数据页写入量。
常见错误现象:Resource group 'rg_slow' does not exist(没创建就直接 SET RESOURCE GROUP)、Access denied; you need (at least one of) the RESOURCE_GROUP_ADMIN privilege(s)(权限不足)。
实操建议:
- 先创建资源组:
CREATE RESOURCE GROUP rg_slow TYPE=USER VCPU=0-1 THREAD_PRIORITY=-10; - 给用户授予权限:
GRANT RESOURCE_GROUP_ADMIN ON *.* TO 'app_user'@'%'; - 在应用连接后立即绑定:
SET RESOURCE GROUP rg_slow;(注意:该语句只对当前会话生效) - 避免绑定到系统线程(如复制线程),否则可能卡住主从同步
性能影响:写入吞吐下降明显,尤其在批量导入或大事务场景;但不会阻止磁盘最终被占满,只是“延缓”——如果业务持续写入且无清理机制,迟早爆盘。
真正管住磁盘:OS 层配额 + MySQL 表空间隔离
MySQL 不管磁盘,但你能管它的数据文件所在目录。这才是最可靠的方式。
使用场景:多租户共用一台 MySQL 实例,每个租户对应一个数据库,且你有服务器 root 权限。
实操建议:
- 为每个租户建独立表空间:
CREATE DATABASE tenant_a DATA DIRECTORY='/data/mysql/tenant_a'; - 在 Linux 上对目录启用磁盘配额:
setquota -u mysql 10G 10G 0 0 /data(需 ext4/xfs + quota 模块启用) - 确认 MySQL 进程属主是
mysql用户,否则配额不生效 - 禁用
innodb_file_per_table=OFF,否则所有表仍写进共享表空间ibdata1,绕过目录配额
兼容性注意:MySQL 5.7 不支持 DATA DIRECTORY 子句(仅 8.0.19+ 支持);Windows 不支持 POSIX 配额,这条路走不通。
为什么别依赖 INFORMATION_SCHEMA.TABLES 算“用户磁盘用量”
很多人想用 SELECT SUM(data_length + index_length) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'db_name' 做实时配额检查——这很危险。
原因如下:
- 结果不准:
data_length是 InnoDB 估算值,不是真实磁盘占用(比如没 purge 的旧版本、空洞页、未刷新的 buffer pool) - 权限问题:
INFORMATION_SCHEMA查询需要PROCESS或SELECT权限,普通应用用户通常没有 - 无法覆盖日志文件:binlog、relay log、slow query log、error log 全都不计入该统计
- 触发锁与扫描开销:大库下执行该查询可能阻塞 DDL 或加重 I/O
真要监控,应该用 OS 工具(du -sh /var/lib/mysql/tenant_a)配合定时任务,而不是在 SQL 层硬算。
复杂点在于:表空间路径、配额策略、资源组绑定这三者必须协同设计,缺一不可;最容易被忽略的是 DATA DIRECTORY 创建后,后续 CREATE TABLE 必须显式指定 TABLESPACE,否则默认还是落在 datadir 下——那一刀就白切了。











