备份单租户库最直接方式是使用mysqldump -u root -p tenant_a > /backup/tenant_a_$(date +%Y%m%d).sql,需确保用户有SELECT权限;租户库名含特殊字符时须用反引号包裹(如tenant-a),生产环境建议加--single-transaction避免锁表影响其他租户。

只备份一个租户的库:用 mysqldump 指定库名最直接
多租户采用「独立数据库」模式时,每个租户对应一个独立库名(比如 tenant_a、tenant_b),这时备份单租户就是备份单个库。不用改表结构、不写过滤逻辑,最稳。
执行命令:mysqldump -u root -p tenant_a > /backup/tenant_a_$(date +%Y%m%d).sql
- 必须确保连接用户有该库的
SELECT权限,否则会报错Access denied for SELECT command - 如果租户库名含特殊字符(如短横线),要用反引号包裹:
`tenant-a`,否则 shell 解析失败 - 生产环境建议加
--single-transaction(InnoDB 表)或--lock-tables=false(避免锁全库),否则备份期间其他租户可能被阻塞
共享库但独立 Schema:用 --all-databases + grep 过滤不现实
当所有租户共用一个数据库、但各自有独立 Schema(如 tenant_a_schema、tenant_b_schema),mysqldump 本身不支持按 Schema 导出——它只认库(database)层级,不认 Schema(MySQL 中 Schema ≡ Database)。所以别试图用 --all-databases | grep tenant_a,会漏掉 CREATE DATABASE 和权限语句,恢复时直接失败。
正确做法是:先查出该租户涉及的所有表名,再拼成 mysqldump 表列表:
mysql -Nse "SELECT CONCAT('tenant_a_schema.', table_name) FROM information_schema.tables WHERE table_schema = 'tenant_a_schema'" | xargs mysqldump -u root -p --databases tenant_a_schema > tenant_a_schema.sql
- 注意
--databases参数必须带上,否则导出文件不含USE `tenant_a_schema`,恢复时可能写进错库 - 如果表数量超 1000,xargs 可能触发参数长度限制,改用
while read循环更稳妥 - Schema 名不能带连字符或下划线开头,否则
information_schema查询可能返回空——检查table_schema字段实际值
共享库+共享表(租户ID字段隔离):mysqldump --where 是唯一可行方案
这种模式下所有租户数据混在一张表里(如 orders 表带 tenant_id 字段),备份单租户只能靠条件过滤。MySQL 原生支持 --where 参数,但限制极多:
示例命令:mysqldump -u root -p myapp orders --where="tenant_id='tenant_a'" > tenant_a_orders.sql
- 仅对单张表生效,多表需逐个执行;且不能跨库,
myapp.orders和myapp.users要分开 dump -
--where值必须是字符串字面量,不能用子查询或变量,tenant_id IN (SELECT id FROM tenants WHERE name='A')会直接报语法错误 - 导出的 SQL 不含
CREATE TABLE,恢复前必须手动建好表结构,否则source tenant_a_orders.sql会提示Table doesn't exist - 大表慎用:WHERE 条件无法利用索引加速导出过程,
mysqldump会全表扫描,500 万行以上可能卡死或超时
恢复时最容易忽略的三件事
无论哪种模式,恢复单租户数据都比备份更易出错:
- 恢复前没清空目标库/表:用
--add-drop-table可自动生成DROP TABLE,但若租户表已存在且有外键约束,会因依赖关系报错Cannot delete or update a parent row - 字符集不一致:备份文件头部的
SET NAMES utf8mb4可能和目标库默认字符集冲突,导致中文变问号;建议显式指定--default-character-set=utf8mb4 - 时间戳字段被覆盖:如果表有
created_at DEFAULT CURRENT_TIMESTAMP,INSERT语句会把原备份时间写死,丢失真实创建时间——除非加--skip-triggers --skip-routines并手写 INSERT
真正麻烦的不是技术动作,而是确认租户边界:一个租户的数据是否真的只在库/表/字段层面隔离?有没有跨租户的汇总视图、定时 job 或 binlog 事件关联?这些地方一旦漏掉,恢复后业务逻辑就断了。


















