MySQL-Shell
MySQL Shell 需要 MySQL 5.7.12 或更高版本的服务器支持基础功能。
MySQL Shell 是 MySQL 官方提供的现代化命令行客户端工具,它提供了强大的数据库备份和恢复功能,相比传统的 mysqldump 工具,MySQL Shell 的备份功能更加高效、灵活。本文将详细介绍如何使用 MySQL Shell 进行数据库备份与恢复操作。
MySQL Shell 概述
- 多语言支持:
- SQL 模式:传统 SQL 命令行
- JavaScript 模式:支持 JS 脚本
- Python 模式:支持 Python 脚本
- 高级功能:
- 数据库对象管理
- 数据备份与恢复
- 文档存储支持
- X Protocol 支持
- 跨平台:
- 支持 Linux、Windows 和 macOS
核心优势
- 并行备份(多线程)
- 压缩支持(zstd/gzip)
- 一致性快照
- 进度跟踪
安装
官网地址:https://downloads.mysql.com/archives/shell/
二进制包
bash
# 下载 MySQL Shell 二进制包
wget https://dev.mysql.com/get/Downloads/MySQL-Shell/mysql-shell-8.0.28-linux-glibc2.12-x86-64bit.tar.gz
# 解压到 /usr/local 目录
tar zxvf mysql-shell-8.0.28-linux-glibc2.12-x86-64bit.tar.gz -C /usr/local/
# 创建软链接
ln -s /usr/local/mysql-shell-8.0.28-linux-glibc2.12-x86-64bit/ /usr/local/mysql-shell
# 添加环境变量
echo 'export PATH=$PATH:/usr/local/mysql-shell/bin' >> ~/.bashrc
source ~/.bashrcRPM包
bash
# 下载rpm包
wget https://downloads.mysql.com/archives/get/p/43/file/mysql-shell-8.0.42-1.el8.x86_64.rpm
# 安装
rpm -ivh mysql-shell-8.0.20-1.el7.x86_64.rpm验证安装
bash
mysqlsh --versionmysqlsh 命令
语法格式
bash
mysqlsh [options] [URI] [-- [script_file] [script_args]]选项
| 选项 | 描述 | 示例 |
|---|---|---|
--uri <connection_string> | 使用URI格式连接MySQL | mysqlsh --uri user@localhost:3306 |
--host <hostname> | 指定服务器主机 | mysqlsh --host 127.0.0.1 |
--port <port> | 指定服务器端口 | mysqlsh --port 33060 |
--user <username> | 指定用户名 | mysqlsh --user root |
--password [password] | 指定密码(可选) | mysqlsh --password |
--py | 启动Python模式 | mysqlsh --py |
--js | 启动JavaScript模式(默认) | mysqlsh --js |
--sql | 启动SQL模式 | mysqlsh --sql |
--file <filename> | 执行脚本文件 | mysqlsh --file setup.js |
--interactive[=full] | 交互模式(默认) | mysqlsh --interactive=full |
--dba enableXProtocol | 启用X协议 | mysqlsh --dba enableXProtocol |
连接 MySQL 服务器
bash
# 方法1:交互式输入密码
mysqlsh root@localhost:3306
# 方法2:命令行指定密码
mysqlsh 'root:password'@localhost:3306
# 方法3:连接时指定数据库
mysqlsh 'root:password'@localhost:3306/testDB
# JavaScript 模式
mysqlsh 'root:password'@localhost:3306 --js
# Python 模式
mysqlsh 'root:password'@localhost:3306 --py
# SQL 模式
mysqlsh 'root:password'@localhost:3306 --sql\help
通过mysqlsh连接到mysql后的选项
| 命令/别名 | 功能描述 |
|---|---|
\help或 \? | 显示帮助信息,列出所有可用命令 |
\quit或 \q | 退出 MySQL Shell |
\exit | 同 \quit,退出 MySQL Shell |
\connect或 \c | 连接到 MySQL 服务器 (格式: user@host:port) |
\disconnect | 断开当前连接 |
\reconnect | 重新连接到上次使用的服务器 |
\sql | 切换到 SQL 执行模式 |
\js | 切换到 JavaScript 执行模式 |
\py | 切换到 Python 执行模式 |
\status或 \s | 显示当前连接状态信息 |
\use或 \u | 设置当前数据库(同 SQL USE语句) |
\source或 \. | 执行外部 SQL 脚本文件 |
\warnings | 启用警告显示 |
\nowarnings | 禁用警告显示 |
\pager | 设置输出分页器(如 less) |
\nopager | 禁用分页器,直接输出到屏幕 |
\history | 显示命令历史记录 |
\show | 显示数据库对象(表、视图等) |
\watch | 定期执行命令并显示结果(类似 watch命令) |
\sleep | 暂停执行指定的秒数 |
\system或 \! | 执行操作系统命令 |
\edit或 \e | 在编辑器中打开当前命令进行编辑 |
\option | 管理 MySQL Shell 配置选项 |
\dump | 导出数据库对象(已弃用,推荐使用 util.dumpInstance()) |
\load | 导入数据(已弃用,推荐使用 util.loadDump()) |
\import | 导入表数据(已弃用,推荐使用 util.importTable()) |
\export | 导出表数据(已弃用,推荐使用 util.exportTable()) |
util | 访问实用工具模块(备份/恢复/导入导出等) |
\dba | 访问 AdminAPI(InnoDB Cluster 管理) |
\cluster | 管理 MySQL InnoDB Cluster |
\rs | 管理 MySQL ReplicaSet |
注意:实际可用命令可能因 MySQL Shell 版本不同而略有差异,建议使用 \help查看您当前版本的完整命令列表。
经典案例
bash
mysqlsh --sql -e "SELECT * FROM testdb.users" > output.txt
# 服务器版本检查
mysqlsh --sql -e "SELECT VERSION()"
# 连接MySQL并执行SQL查询
mysqlsh --sql --user root --host localhost --port 3306 --schema test -e "SELECT * FROM users"
# 使用JavaScript模式创建文档集合
mysqlsh --js
\connect user:pwd@localhost:33060
session.createSchema('my_collection')
db = session.getSchema('my_collection')
db.createCollection('customers')
# 使用Python模式批量导入数据
mysqlsh --py
import mysqlx
session = mysqlx.get_session('user:pwd@localhost:33060')
schema = session.get_schema('test')
table = schema.get_table('employees')
table.insert(['name','age']).values('John',30).execute()多语言支持
MySQL Shell 支持三种语言模式,可随时切换:
切换语言模式:
sql\sql # 切换到SQL模式 \js # 切换到JavaScript模式 \py # 切换到Python模式JavaScript示例:
javascriptvar db = session.createSchema('testDB'); var sqlCreateTable = "CREATE TABLE testDB.myTable (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255))"; var result = session.sql(sqlCreateTable).execute();Python示例:
pythonsession.run_sql('CREATE DATABASE IF NOT EXISTS mydb') session.run_sql('USE mydb') session.run_sql('CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50), age INT)')
util-数据备份
备份级别
- 实例级备份 (
dumpInstance) - 数据库级备份 (
dumpSchemas) - 表级备份 (
dumpTables)
选项
通用备份选项(适用于所有备份方法)
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
threads | int | 4 | 并行线程数 |
compression | string | "zstd" | 压缩算法:"none"、"gzip"、"zstd" |
showProgress | bool | true | 显示进度条 |
dryRun | bool | false | 模拟执行(不实际备份) |
chunking | bool | true | 启用表分块 |
bytesPerChunk | string | "64M" | 每个分块的最大字节数 |
maxRate | string | "0" | 最大传输速率(如 "100M") |
showMetadata | bool | true | 在输出中包含元数据 |
ocimds | bool | false | 为Oracle Cloud Infrastructure MySQL服务优化 |
compatibility | array | [] | 兼容性选项列表(如 ["force_innodb"]) |
where | dict | {} | 表过滤条件(如 {"schema.table": "id>1000"}) |
ddlOnly | bool | false | 仅备份DDL(不备份数据) |
dataOnly | bool | false | 仅备份数据(不备份DDL) |
events | bool | true | 备份事件 |
routines | bool | true | 备份存储过程和函数 |
triggers | bool | true | 备份触发器 |
tzUtc | bool | true | 使用UTC时区 |
skipConsistencyChecks | bool | false | 跳过一致性检查 |
consistent | bool | true | 获取一致性快照(使用FLUSH TABLES WITH READ LOCK) |
dummy | bool | false | 生成虚拟备份(测试用) |
dumpInstance() 特有选项
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
users | bool | true | 备份用户账户 |
includeSchemas | array | null | 包含的数据库列表 |
excludeSchemas | array | null | 排除的数据库列表 |
includeTables | array | null | 包含的表列表 |
excludeTables | array | null | 排除的表列表 |
schema | string | null | 仅备份指定schema |
dumpSchemas() 特有选项
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
includeTables | array | null | 包含的表列表 |
excludeTables | array | null | 排除的表列表 |
dumpTables() 特有选项
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
all | bool | false | 备份所有表(忽略表列表) |
partitions | array | null | 指定分区列表 |
util.loadDump() 导入选项
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
loadIndexes | bool | true | 是否加载索引 |
deferTableIndexes | string | "off" | 延迟索引创建:"off"、"all"、"fulltext" |
loadDdl | bool | true | 是否执行DDL语句 |
loadData | bool | true | 是否加载数据 |
skipBinlog | bool | false | 跳过binlog记录(加速导入) |
schema | string | null | 指定导入到特定schema |
ignoreVersion | bool | false | 忽略MySQL版本检查 |
resetProgress | bool | false | 重置之前的导入进度 |
background | bool | false | 后台加载数据 |
characterSet | string | null | 指定字符集(如 "utf8mb4") |
createInvisiblePKs | bool | false | 为无主键表创建隐藏主键(MySQL 8.0+) |
ignoreExistingObjects | bool | false | 跳过已存在的对象 |
stripDefiners | bool | false | 移除DEFINER子句 |
analyzeTables | bool | false | 导入后执行ANALYZE TABLE |
osBucketName | string | null | OCI存储桶名称 |
osNamespace | string | null | OCI命名空间 |
SQL 模式备份
sql
-- 设置备份选项(需先切换到JS或PY模式设置变量)
\js
var options = {
threads: 4,
compression: "zstd",
showProgress: true
}
\sql
-- 执行备份(实际仍调用JS/PY函数)
\js util.dumpInstance("/backup/full", options);注意:纯SQL模式无法直接使用util工具,需借助JS/PY模式
JavaScript 模式备份
全实例备份
javascript
util.dumpInstance("/backup/full", {
threads: 8, // 并行线程数
compression: "zstd", // 压缩算法(zstd/gzip/none)
showProgress: true, // 显示进度条
ocimds: true, // 为MySQL云服务优化
compatibility: ["strip_restricted_grants"], // 兼容性选项
users: true // 包含用户账户
});单数据库备份
javascript
util.dumpSchemas(["employees"], "/backup/employees", {
excludeTables: ["employees.salaries"], // 排除特定表
ddlOnly: false, // 仅结构
dataOnly: false, // 仅数据
chunking: true // 启用分块
});大表备份(分块)
javascript
util.dumpTables("sakila", ["film", "film_actor"], "/backup/sakila_films", {
bytesPerChunk: "64M", // 每块大小
where: {"sakila.film": "rating='PG-13'"} // 条件过滤
});分库备份+并行处理
javascript
// 分库备份+并行处理
const databases = ["db1", "db2", "db3"];
databases.forEach(db => {
util.dumpSchemas([db], `/backup/${db}`, {
threads: 4,
compression: "zstd"
});
});完整示例
javascript
util.dumpInstance("/backup/full", {
// 通用选项
threads: 8,
compression: "zstd",
showProgress: true,
// 实例备份选项
users: true,
excludeSchemas: ["information_schema", "performance_schema"],
excludeTables: ["mysql.user"],
// 高级选项
compatibility: ["strip_restricted_grants", "force_innodb"],
where: {
"employees.salaries": "salary > 100000",
"employees.dept_manager": "1=1"
},
// 分块控制
chunking: true,
bytesPerChunk: "128M",
// 内容过滤
ddlOnly: false,
dataOnly: false,
routines: true,
triggers: true
});Python 模式备份
全实例备份
python
util.dump_instance("/backup/full", {
"threads": 4,
"compression": "gzip",
"showProgress": True,
"dryRun": False # 模拟执行
})增量备份(需先做全量备份)
python
util.dump_instance("/backup/incr", {
"baseDir": "/backup/full", # 基于全量备份
"incremental": True,
"lastSeen": "2023-11-20T15:00:00" # 上次备份时间
})备份验证
python
# 验证备份完整性
util.check_for_server_upgrade({
"targetVersion": "8.0.36",
"configPath": "/backup/full/@.json",
"outputFormat": "JSON"
})全量备份脚本
可在定时任务中设定每天凌晨2点全量备份0 2 * * * /usr/bin/mysqlsh --py -f /scripts/mysql_backup.py
python
#!/usr/bin/env mysqlsh
# -*- coding: utf-8 -*-
import os
import time
import smtplib
from email.mime.text import MIMEText
from datetime import datetime
# 配置部分
CONFIG = {
"backup_dir": "/data/backups/mysql",
"retention_days": 7,
"mysql_uri": "root:password@localhost:3306",
"compress": "zstd",
"threads": 4,
"schemas": ["important_db", "app_db"], # 空列表表示全库备份
"exclude_tables": ["app_db.log_data"], # 排除的表
"smtp": {
"enabled": True,
"host": "smtp.example.com",
"port": 587,
"user": "backup@example.com",
"password": "email_password",
"to": "dba@example.com"
}
}
def send_email(subject, body):
if not CONFIG["smtp"]["enabled"]:
return
msg = MIMEText(body)
msg['Subject'] = subject
msg['From'] = CONFIG["smtp"]["user"]
msg['To'] = CONFIG["smtp"]["to"]
try:
with smtplib.SMTP(CONFIG["smtp"]["host"], CONFIG["smtp"]["port"]) as server:
server.starttls()
server.login(CONFIG["smtp"]["user"], CONFIG["smtp"]["password"])
server.send_message(msg)
except Exception as e:
log(f"邮件发送失败: {str(e)}", level="ERROR")
def log(message, level="INFO"):
log_entry = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {level}: {message}"
print(log_entry)
log_file = os.path.join(CONFIG["backup_dir"], "backup.log")
with open(log_file, "a") as f:
f.write(log_entry + "\n")
def cleanup_old_backups():
now = time.time()
cutoff = now - (CONFIG["retention_days"] * 86400)
deleted = 0
for filename in os.listdir(CONFIG["backup_dir"]):
filepath = os.path.join(CONFIG["backup_dir"], filename)
if os.path.isdir(filepath) and filename.startswith("backup_"):
file_time = os.path.getmtime(filepath)
if file_time < cutoff:
log(f"删除过期备份: {filename}")
os.system(f"rm -rf {filepath}")
deleted += 1
return deleted
def perform_backup():
# 创建备份目录
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = os.path.join(CONFIG["backup_dir"], f"backup_{timestamp}")
os.makedirs(backup_path, exist_ok=True)
log(f"开始备份到 {backup_path}")
start_time = time.time()
try:
# 备份选项
options = {
"threads": CONFIG["threads"],
"compression": CONFIG["compress"],
"showProgress": False,
"consistent": True
}
if CONFIG["exclude_tables"]:
options["excludeTables"] = CONFIG["exclude_tables"]
# 执行备份
if CONFIG["schemas"]:
util.dump_schemas(CONFIG["schemas"], backup_path, options)
else:
util.dump_instance(backup_path, options)
# 计算备份大小
size = sum(os.path.getsize(os.path.join(dirpath, filename))
for dirpath, _, filenames in os.walk(backup_path)
for filename in filenames) / (1024 * 1024)
elapsed = time.time() - start_time
log(f"备份成功完成! 大小: {size:.2f} MB, 耗时: {elapsed:.2f} 秒")
# 发送成功通知
send_email(
"MySQL备份成功通知",
f"备份已成功完成:\n路径: {backup_path}\n大小: {size:.2f} MB\n耗时: {elapsed:.2f} 秒"
)
return True
except Exception as e:
log(f"备份失败: {str(e)}", level="ERROR")
send_email(
"MySQL备份失败通知",
f"备份失败:\n错误: {str(e)}\n时间: {datetime.now()}"
)
return False
if __name__ == "__main__":
# 初始化MySQL连接
shell.connect(CONFIG["mysql_uri"])
# 执行备份和清理
backup_status = perform_backup()
deleted = cleanup_old_backups()
log(f"已清理 {deleted} 个过期备份")
log("备份任务完成\n" + "="*50)增量备份脚本
可在定时任务中设定每小时增量备份 0 * * * * /usr/bin/mysqlsh --py -f /scripts/incremental_backup.py
python
#!/usr/bin/env mysqlsh
import os
import datetime
from mysqlsh import globals
def incremental_backup():
config = {
"base_dir": "/data/backups/mysql/full_backup",
"incr_dir": "/data/backups/mysql/incremental",
"mysql_uri": "root:password@localhost:3306"
}
# 获取最近的全量备份
last_full = sorted([d for d in os.listdir(config["base_dir"])
if d.startswith("backup_")])[-1]
base_path = os.path.join(config["base_dir"], last_full)
# 创建增量备份目录
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
incr_path = os.path.join(config["incr_dir"], f"incr_{timestamp}")
os.makedirs(incr_path, exist_ok=True)
# 连接MySQL
globals.shell.connect(config["mysql_uri"])
# 执行增量备份
util.dump_instance(incr_path, {
"baseDir": base_path,
"incremental": True,
"threads": 4,
"compression": "zstd"
})
if __name__ == "__main__":
incremental_backup()util-恢复数据
JavaScript 恢复
javascript
util.loadDump("/backup/full", {
// 基本参数
threads: 8, // 启动线程
showProgress: true, // 显示进度
// 数据控制
loadData: true, // 明确启用数据加载
skipBinlog: true, // 跳过binlog记录
bytesPerChunk: "64M", // 数据分块大小
// DDL控制
loadDdl: true,
createInvisiblePKs: true, // 避免与自动建库冲突
deferTableIndexes: "all", // 延迟索引创建:"off" | "all" | "fulltext"
// 错误处理
ignoreExistingObjects: false, // 忽略已存在对象
resetProgress: true, // 清除之前的错误进度
// 字符集
characterSet: "utf8mb4"
});Python 恢复
python
util.load_dump("/backup/full", {
# 基本参数
"threads": 8, # 启动线程
"showProgress": True, # 显示进度
# 数据控制
"loadData": True, # 明确启用数据加载
"skipBinlog": True, # 跳过binlog记录
"bytesPerChunk": "64M", # 数据分块大小
# DDL控制
"loadDdl": True,
"createInvisiblePKs": True, # 避免与自动建库冲突
"deferTableIndexes": "all", # 延迟索引创建:"off" | "all" | "fulltext"
# 错误处理
"ignoreExistingObjects": False, # 忽略已存在对象
"resetProgress": True, # 清除之前的错误进度
# 字符集
"characterSet": "utf8mb4"
});
util.load_dump("/tmp/backup", {
"threads": 8,
"showProgress": True,
"loadData": True,
"skipBinlog": True,
"deferTableIndexes": "all",
"characterSet": "utf8mb4",
"resetProgress": True # 添加此参数以防之前有失败的导入
})常见问题解决
内存不足:
javascriptutil.dumpInstance("/backup/full", { threads: 2, // 减少线程 maxBytesPerTransaction: "1G" // 限制事务大小 });网络中断恢复:
pythonutil.load_dump("/backup/full", { "resetProgress": True, # 重置进度 "skipConsistencyChecks": True # 跳过一致性检查 })版本兼容性:
javascriptutil.dumpInstance("/backup/full", { compatibility: [ "strip_restricted_grants", "force_innodb", "strip_definers" ] });local_infile安全限制
local_infile参数控制是否允许从客户端机器加载文件数据,默认关闭是防止恶意文件注入。MySQL 8.0+ 对此有更严格的安全要求。sql-- 临时解决 SET GLOBAL local_infile = ON; -- 导入完成后建议恢复设置 SET GLOBAL local_infile = OFF; -- 永久解决方案(需重启) -- 修改配置文件 my.cnf/my.ini [mysqld] local_infile = ON -- 然后重启MySQL服务
