Windows Server 定时备份 MySQL 数据升级版:单表备份 + 压缩功能 + 运维统计
《Windows Server 定时备份 MySQL 数据升级版:单表备份 + 压缩功能 + 运维统计》
1. 升级功能点
1.1 单表备份功能
功能亮点
- 细粒度备份:可指定备份特定数据库的特定表。
- 配置驱动:通过
SINGLE_TABLES
变量动态解析备份目标。 - 独立文件存储:每个表生成单独备份文件(
数据库名_表名_日期.sql
)。
if /i "%IS_SINGLE_TABLES%"=="1" (for /f "tokens=1* delims=;" %%a in ("%SINGLE_TABLES%") do (set "current_pair=%%a"...for %%t in (!tables!) do ("%MYSQL_BIN%" ... !danbiao_db! %%t > "!danbiao_db!_%%t_%Ymd%.sql"))
)
1.2 压缩功能
优化点
- 多工具支持:优先级检测 WinRAR → Bandizip → 7-Zip。
- 参数化压缩:根据工具类型动态生成压缩命令。
- 空间优化:压缩成功后自动清理原始 SQL 文件。
if /i "%IS_ZIP%"=="1" (:: 自动检测可用压缩工具(WinRAR > Bandizip > 7-Zip)if exist "%WINRAR_PATH%" ( ... )else if exist "%BANDZIP_PATH%" ( ... )else ( ... ):: 压缩后删除原SQL文件!COMPRESS_CMD! "!zip_file!" "!sql_file!"if !ERRORLEVEL! equ 0 del /q "!sql_file!"
)
1.3 健壮性增强
- 预检机制
:: 检查 MySQL 客户端是否存在
if not exist "%MYSQL_BIN_TEST%" ( ... exit /b 2 ):: 测试数据库连接
"%MYSQL_BIN_TEST%" -h%MYSQL_HOST% ... -e "SELECT @@version;"
if %ERRORLEVEL% NEQ 0 ( ... exit /b 1 )
- 目录轮转优化
:: 使用 robocopy 替代 move(更可靠的空目录处理)
robocopy "!source_dir!" "!target_dir!" ... /mov /e /njh /njs
- 错误处理
set ERROR_FLAG=0 :: 全局错误标记
if !ERRORLEVEL! GTR 1 set ERROR_FLAG=1
1.4 备份统计
:: 计算备份耗时(精确到毫秒)
call :calculate_backup_time → 输出 %TOTAL_ELAPSED_TIME%:: 统计备份目录大小(智能单位转换)
call :get_folder_size "%BACKUP_ROOT%D7" → 输出 %FOLDER_SIZE%
2. 完整的备份脚本
@echo off
setlocal enabledelayedexpansion:: ================================================
:: 自动请求管理员权限
:: ================================================
:: 检查是否已管理员身份运行
net session >nul 2>&1
if %ERRORLEVEL% NEQ 0 (echo 正在请求管理员权限...powershell -Command "Start-Process '%~s0' -Verb RunAs"exit /b
):: ================================================
:: 数据库备份脚本(优化版)
:: 功能:自动备份MySQL数据库,保留7天历史备份
:: 特点:WinRAR压缩、并行执行、错误日志、权限控制
:: 配置日期:2025-09-05
:: ================================================:: ------------------------------
:: 配置区域(根据实际环境修改)
:: 初始化变量(需要根据实际环境修改)
:: ------------------------------
set "MYSQL_BIN=G:\SoftwareWork\mysql\bin\mysqldump"
set "MYSQL_BIN_TEST=G:\SoftwareWork\mysql\bin\mysql.exe":: 需要确保备份的用户(root)有导出权限功能
set "DATABASE_USER=root"
:: 如果密码带有特殊字符,需要在特殊字符前面加上^来转义,确保密码可以被系统正确读取
set "DATABASE_PASSWORD=123456"
:: 默认本地IP,可改为远程服务器IP
set "MYSQL_HOST=127.0.0.1":: 是否开启全库备份功能 1 开启 0 不开启
set "IS_QUANKU_BEIFEN=1"
:: 数据库名称,中间通过英文 , 进行分隔
set "DATABASES=app,client,plat":: 配置区域新增单表备份配置
:: 是否开启单表备份 1 开启单表备份 0 不开启单表备份
set "IS_SINGLE_TABLES=1"
:: 格式:数据库名1:表名1,表名2;数据库名2:表名1,表名2
set "SINGLE_TABLES=app:studen":: 是否启用压缩,1 启用 0 不启用
:: 压缩软件安装目录,支持WinRAR、BandZIP、7ZIP
:: 首选的压缩软件是WinRAR
set "IS_ZIP=1"
set "WINRAR_PATH=G:\Program Files\WinRAR\WinRAR.exe"
set "BANDZIP_PATH="
set "7ZIP_PATH=":: ------------------------------
:: 配置区域(不需要修改)
:: 初始化变量
:: ------------------------------
:: 当前文件夹
set "BACKUP_ROOT=%~dp0"
:: 年月
set "Ym=%date:~,4%%date:~5,2%"
:: 年月日
set "Ymd=%date:~,4%%date:~5,2%%date:~8,2%"
:: 日志目录
set "LOG_DIR=%BACKUP_ROOT%logs"
:: 备份日志文件,每个月一份日志文件
set "LOGFILE=%BACKUP_ROOT%logs/backup_%Ym%.log"
set ERROR_FLAG=0
:: 记录备份开始时间
set "BACKUP_START_TIME=%time%"
set "BACKUP_START_DATE=%date%"set BACKUP_DIR="%BACKUP_ROOT%D1":: 创建日志目录
if not exist "%LOG_DIR%" (mkdir "%LOG_DIR%"
)echo ====================================================================================================================== >> %LOGFILE%:: 验证mysqldump可用性
:: 检查是否在PATH上配置MYSQL_HOME路径(例如 G:\SoftwareWork\mysql\bin)
echo [%date% %time%] ========== 验证MYSQL_BIN、MYSQL_BIN_TEST开始 ========== >> "%LOGFILE%"
where mysqldump >nul 2>&1
if %errorlevel% neq 0 (echo [%date% %time%] 错误: mysqldump未找到 >> "%LOGFILE%"exit /b 1
) else (echo [%date% %time%] 路径: %MYSQL_BIN%存在 >> "%LOGFILE%"
):: 检查mysql是否存在
if not exist "%MYSQL_BIN_TEST%" (echo [%date% %time%] 错误: MySQL客户端未找到 >> "%LOGFILE%"echo 路径: %MYSQL_BIN_TEST% >> "%LOGFILE%"exit /b 2
)else (echo [%date% %time%] 路径: %MYSQL_BIN_TEST%存在 >> %LOGFILE%
)
echo [%date% %time%] ========== 验证MYSQL_BIN、MYSQL_BIN_TEST结束 ========== >> "%LOGFILE%"echo [%date% %time%] ================== 开始目录检查 ================== >> %LOGFILE%
:: ------------------------------
:: 目录检查与创建
:: ------------------------------
for /l %%i in (1,1,7) do (if not exist %BACKUP_ROOT%D%%i (mkdir %BACKUP_ROOT%D%%iecho [%date% %time%] 提示:创建目录: D%%i >> %LOGFILE%)
):: ------------------------------
:: 清理旧备份(保留7天)
:: ------------------------------
echo [%date% %time%] ======================== 清理旧备份开始 ======================== >> "%LOGFILE%"
:: 检查目录是否存在
if not exist %BACKUP_DIR% (echo [%date% %time%] 警告: 目录 %BACKUP_DIR% 不存在 >> "%LOGFILE%"goto :skip_delete
):: 检查目录是否为空
dir /a-d /b %BACKUP_DIR%\* >nul 2>&1
if %ERRORLEVEL% neq 0 (echo [%date% %time%] 警告: %BACKUP_DIR% 无文件可删除 >> "%LOGFILE%"goto :skip_delete
):: 删除所有文件
echo [%date% %time%] 正在清理 %BACKUP_DIR% 目录下的所有文件... >> "%LOGFILE%"
del /Q /F %BACKUP_DIR%\* >> "%LOGFILE%" 2>&1if %ERRORLEVEL% neq 0 (echo [%date% %time%] 错误: 目录文件删除 %BACKUP_DIR% 失败 (错误码: %ERRORLEVEL%) >> "%LOGFILE%"set ERROR_FLAG=1
) else (echo [%date% %time%] 成功: 目录文件已清理 %BACKUP_DIR% >> "%LOGFILE%"
):skip_delete
echo [%date% %time%] ======================== 清理旧备份结束 ======================== >> "%LOGFILE%":: ------------------------------
:: 目录轮转(使用robocopy更可靠)
:: ------------------------------
echo [%date% %time%] ================== 开始目录轮转 ================== >> %LOGFILE%
for /l %%i in (2,1,7) do (set /a j=%%i-1set "source_dir=%BACKUP_ROOT%D%%i"set "target_dir=%BACKUP_ROOT%D!j!":: 使用robocopy移动文件(自动处理空目录)robocopy "!source_dir!" "!target_dir!" *.* /mov /e /njh /njs /ndl /nc /ns /nfl >nul 2>&1if !ERRORLEVEL! GTR 1 (echo [%date% %time%] 错误: 移动D%%i到D!j!失败(错误码: !ERRORLEVEL!) >> %LOGFILE%set ERROR_FLAG=1) else (echo [%date% %time%] 成功: D%%i文件已移动到D!j! >> %LOGFILE%)
)
echo [%date% %time%] ================== 结束目录检查 ================== >> %LOGFILE%echo [%date% %time%] ======================== 备份开始 ======================== >> "%LOGFILE%"echo [%date% %time%] ================== 测试数据库连接开始 ================== >> "%LOGFILE%"
:: 测试数据库连接
"%MYSQL_BIN_TEST%" -h%MYSQL_HOST% -u"%DATABASE_USER%" -p"%DATABASE_PASSWORD%" -e "SELECT @@version;" >nul 2>&1
if %ERRORLEVEL% equ 0 (echo [%date% %time%] MySQL服务正常 >> "%LOGFILE%"
) else (echo [%date% %time%] 失败 错误代码: %ERRORLEVEL% >> "%LOGFILE%"echo [%date% %time%] 请检查: >> "%LOGFILE%"echo 1. MySQL服务是否运行 >> "%LOGFILE%"echo 2. 用户名/密码是否正确 >> "%LOGFILE%"echo 3. 防火墙是否放行3306端口 >> "%LOGFILE%"exit /b 1
)
echo [%date% %time%] ================== 测试数据库连接结束 ================== >> "%LOGFILE%"echo ========================================================================================== >> "%LOGFILE%"
:: ==================== 单表备份功能 ====================
echo [%date% %time%] 检查单表备份功能设置 IS_SINGLE_TABLES=%IS_SINGLE_TABLES% >> "%LOGFILE%"
:: 清理变量值中的空格
set "IS_SINGLE_TABLES=%IS_SINGLE_TABLES: =%"
if /i "%IS_SINGLE_TABLES%"=="1" (:: 单表备份(单独备份重要的表)echo [%date% %time%] ================== 单表备份开始 ================== >> "%LOGFILE%":: 解析单表备份配置for /f "tokens=1* delims=;" %%a in ("%SINGLE_TABLES%") do (set "current_pair=%%a"set "remaining_pairs=%%b"echo [%date% %time%] 准备备份数据库: "!current_pair!" 下的单表 >> "%LOGFILE%":process_pairfor /f "tokens=1* delims=:" %%d in ("!current_pair!") do (set "danbiao_db=%%d"set "tables=%%e":: 备份该数据库下的所有指定表for %%t in (!tables!) do (echo [%date% %time%] 开始备份单表: !danbiao_db!.%%t >> "%LOGFILE%""%MYSQL_BIN%" -h%MYSQL_HOST% -u"%DATABASE_USER%" -p"%DATABASE_PASSWORD%" !danbiao_db! %%t ^> "%BACKUP_ROOT%D7\!danbiao_db!_%%t_%Ymd%.sql" 2>> "%LOGFILE%"if !ERRORLEVEL! equ 0 (echo [%date% %time%] 单表备份成功: !danbiao_db!.%%t 已备份到 D7\!danbiao_db!_%%t_%Ymd%.sql >> "%LOGFILE%") else (echo [%date% %time%] 单表备份失败: !danbiao_db!.%%t 备份错误(代码!ERRORLEVEL!) >> "%LOGFILE%"set ERROR_FLAG=1))):: 处理下一对数据库:表if defined remaining_pairs (set "current_pair=!remaining_pairs!"set "remaining_pairs="goto process_pair))echo [%date% %time%] ================== 单表备份结束 ================== >> "%LOGFILE%"
) else (echo [%date% %time%] 单表备份功能未启用(IS_SINGLE_TABLES=%IS_SINGLE_TABLES%) >> "%LOGFILE%"
)echo ========================================================================================== >> "%LOGFILE%"
:: 全库备份
echo [%date% %time%] 检查全库备份功能设置 IS_QUANKU_BEIFEN=%IS_QUANKU_BEIFEN% >> "%LOGFILE%"
:: 清理变量值中的空格
set "IS_QUANKU_BEIFEN=%IS_QUANKU_BEIFEN: =%"
if /i "%IS_QUANKU_BEIFEN%"=="1" (echo [%date% %time%] ================== 全库备份开始 ================== >> "%LOGFILE%"for %%d in (%DATABASES%) do (set "current_db=%%d"echo [%date% %time%] 开始备份: !current_db! >> "%LOGFILE%":: 核心备份命令(显式指定-h)"%MYSQL_BIN%" -h%MYSQL_HOST% -u"%DATABASE_USER%" -p"%DATABASE_PASSWORD%" ^--single-transaction --source-data=2 --routines --triggers --events %%d ^> "%BACKUP_ROOT%D7\!current_db!_%Ymd%.sql" 2>> "%LOGFILE%"if !ERRORLEVEL! equ 0 (echo [%date% %time%] 成功: !current_db! 已备份到 D7\!current_db!_%Ymd%.sql >> "%LOGFILE%") else (echo [%date% %time%] 失败: !current_db! 备份错误(代码!ERRORLEVEL!) >> "%LOGFILE%"type "%LOGFILE%" | findstr /i "error" | tail -n 3 >> "%LOGFILE%"))echo [%date% %time%] ================== 全库备份结束 ================== >> "%LOGFILE%"
) else (echo [%date% %time%] 全库备份功能未启用(IS_QUANKU_BEIFEN=%IS_QUANKU_BEIFEN%) >> "%LOGFILE%"
)echo ========================================================================================== >> "%LOGFILE%"
:: ==================== 压缩功能 ====================
echo [%date% %time%] 检查压缩设置 IS_ZIP=%IS_ZIP% >> "%LOGFILE%"
:: 清理变量值中的空格
set "IS_ZIP=%IS_ZIP: =%"
if /i "%IS_ZIP%"=="1" (echo [%date% %time%] ===================== 开始压缩备份文件 ===================== >> "%LOGFILE%":: 检测可用的压缩工具(按优先级排序)set "COMPRESS_TOOL="set "COMPRESS_CMD=":: 1. 检测WinRARif exist "%WINRAR_PATH%" (set "COMPRESS_TOOL=WinRAR"set "COMPRESS_CMD="%WINRAR_PATH%" a -afzip -ep1 -ibck -inul -m5" ) else (:: 2. 检测Bandizipif exist "%BANDZIP_PATH%" (set "COMPRESS_TOOL=Bandizip"set "COMPRESS_CMD="%BANDZIP_PATH%" c -fmt:zip -y") else (:: 3. 检测7-Zip(如果以后需要支持)where 7z >nul 2>&1if !ERRORLEVEL! equ 0 (set "COMPRESS_TOOL=7-Zip"set "COMPRESS_CMD=7z a -tzip -mx5"))):: 如果没有检测到任何压缩工具if not defined COMPRESS_TOOL (echo [%date% %time%] 错误: 未找到支持的压缩工具 >> "%LOGFILE%"echo [%date% %time%] 请安装WinRAR、Bandizip、7-ZIP >> "%LOGFILE%"set ERROR_FLAG=1echo [%date% %time%] ===================== 结束压缩备份文件 ===================== >> "%LOGFILE%") else (echo [%date% %time%] 压缩工具: !COMPRESS_TOOL! >> "%LOGFILE%"echo [%date% %time%] 压缩参数: !COMPRESS_CMD! >> "%LOGFILE%":: 执行压缩操作echo [%date% %time%] 正在使用!COMPRESS_TOOL!压缩D7目录下的SQL文件 >> "%LOGFILE%":: 压缩每个SQL文件为单独的ZIPfor %%f in ("%BACKUP_ROOT%D7\*.sql") do (set "sql_file=%%f"set "zip_file=%%~nf.zip"echo [%date% %time%] sql文件正在压缩: !sql_file! >> "%LOGFILE%"echo [%date% %time%] 压缩命令为: !COMPRESS_CMD! "%BACKUP_ROOT%D7\!zip_file!" "!sql_file!" >> "%LOGFILE%"!COMPRESS_CMD! "%BACKUP_ROOT%D7\!zip_file!" "!sql_file!"if !ERRORLEVEL! equ 0 (del /q "!sql_file!"echo [%date% %time%] sql文件压缩成功: !zip_file! >> "%LOGFILE%") else (echo [%date% %time%] sql文件压缩失败: !sql_file! (错误码:!ERRORLEVEL!) >> "%LOGFILE%"set ERROR_FLAG=1))echo [%date% %time%] ===================== 结束压缩备份文件 ===================== >> "%LOGFILE%")
) else (echo [%date% %time%] 压缩功能未启用(IS_ZIP=%IS_ZIP%) >> "%LOGFILE%"
):: ==================== 备份统计功能 ====================
echo ========================================================================================== >> "%LOGFILE%"
echo [%date% %time%] ======================== 开始统计备份统计信息 ======================== >> "%LOGFILE%":: 1. 计算总备份时间
call :calculate_backup_time:: 2. 统计D7文件夹大小
call :get_folder_size "%BACKUP_ROOT%D7":: 3. 输出统计结果
echo [%date% %time%] ============================================== >> "%LOGFILE%"
echo [%date% %time%] 备份开始时间: %BACKUP_START_DATE% %BACKUP_START_TIME% >> "%LOGFILE%"
echo [%date% %time%] 备份结束时间: %date% %time% >> "%LOGFILE%"
echo [%date% %time%] 总备份耗时: %TOTAL_ELAPSED_TIME% >> "%LOGFILE%"
echo [%date% %time%] D7文件夹大小: %FOLDER_SIZE% >> "%LOGFILE%"
echo [%date% %time%] ============================================== >> "%LOGFILE%"
echo [%date% %time%] ======================== 结束统计备份统计信息 ======================== >> "%LOGFILE%"echo [%date% %time%] ======================== 备份完成 ======================== >> "%LOGFILE%":: ------------------------------
:: 计算备份总耗时的辅助函数
:: ------------------------------
:calculate_backup_time:: 解析开始时间for /f "tokens=1-4 delims=:.," %%a in ("%BACKUP_START_TIME%") do (set "start_h=%%a"set "start_m=%%b"set "start_s=%%c"set "start_ms=%%d"):: 解析结束时间for /f "tokens=1-4 delims=:.," %%a in ("%time%") do (set "end_h=%%a"set "end_m=%%b"set "end_s=%%c"set "end_ms=%%d"):: 转换为秒数进行计算set /a start_total=1000*(1%start_h%-100)*3600 + 1000*(1%start_m%-100)*60 + 1000*(1%start_s%-100) + (1%start_ms%-100)set /a end_total=1000*(1%end_h%-100)*3600 + 1000*(1%end_m%-100)*60 + 1000*(1%end_s%-100) + (1%end_ms%-100):: 计算时间差(毫秒)set /a elapsed_ms=end_total - start_total:: 处理跨天情况if %elapsed_ms% lss 0 set /a elapsed_ms+=86400000:: 转换回小时:分钟:秒.毫秒格式set /a elapsed_s=elapsed_ms / 1000set /a elapsed_ms=elapsed_ms %% 1000set /a elapsed_m=elapsed_s / 60set /a elapsed_s=elapsed_s %% 60set /a elapsed_h=elapsed_m / 60set /a elapsed_m=elapsed_m %% 60:: 格式化输出(补前导零)if %elapsed_h% lss 10 set elapsed_h=0%elapsed_h%if %elapsed_m% lss 10 set elapsed_m=0%elapsed_m%if %elapsed_s% lss 10 set elapsed_s=0%elapsed_s%if %elapsed_ms% lss 100 set elapsed_ms=0%elapsed_ms%if %elapsed_ms% lss 10 set elapsed_ms=0%elapsed_ms%set "TOTAL_ELAPSED_TIME=%elapsed_h%:%elapsed_m%:%elapsed_s%.%elapsed_ms%"
goto :eof:: ------------------------------
:: 获取文件夹大小的辅助函数(增强版)
:: ------------------------------
:get_folder_sizeset "folder=%~1"set "FOLDER_SIZE=0"set "raw_size="echo [%date% %time%] 开始计算文件夹大小: "%folder%" >> "%LOGFILE%":: 使用增强的PowerShell命令,处理特殊路径和错误for /f "usebackq delims=" %%a in (`powershell -Command "$ErrorActionPreference='Stop'; try { (Get-ChildItem -Path '%folder%' -Recurse -File -ErrorAction Stop | Measure-Object -Property Length -Sum).Sum } catch { Write-Host 'ERROR'; }"`) do (set "raw_size=%%a"):: 检查是否获取到有效大小if defined raw_size (if "!raw_size!"=="ERROR" (echo [%date% %time%] 错误: 无法访问文件夹 "%folder%" >> "%LOGFILE%"set "FOLDER_SIZE=访问失败") else if "!raw_size!"=="" (echo [%date% %time%] 警告: 文件夹为空或未找到文件 >> "%LOGFILE%"set "FOLDER_SIZE=0 B") else (:: 转换为人类可读格式(增强版)set "bytes=!raw_size!":: 使用PowerShell进行单位转换(更可靠)for /f "usebackq delims=" %%s in (`powershell -Command "[math]::Round(!bytes!/1GB,2) | Out-String"`) do set "gb_size=%%s"for /f "usebackq delims=" %%s in (`powershell -Command "[math]::Round(!bytes!/1MB,2) | Out-String"`) do set "mb_size=%%s"for /f "usebackq delims=" %%s in (`powershell -Command "[math]::Round(!bytes!/1KB,2) | Out-String"`) do set "kb_size=%%s":: 选择合适的单位显示if !bytes! gtr 1073741824 (set "FOLDER_SIZE=!gb_size: =! GB") else if !bytes! gtr 1048576 (set "FOLDER_SIZE=!mb_size: =! MB") else if !bytes! gtr 1024 (set "FOLDER_SIZE=!kb_size: =! KB") else (set "FOLDER_SIZE=!bytes! B")echo [%date% %time%] 原始大小: !bytes! 字节 >> "%LOGFILE%"echo [%date% %time%] 转换结果: !FOLDER_SIZE! >> "%LOGFILE%")):: 最终检查if not defined FOLDER_SIZE set "FOLDER_SIZE=计算失败"if "!FOLDER_SIZE!"=="0" set "FOLDER_SIZE=0 B"echo [%date% %time%] 文件夹大小计算完成: !FOLDER_SIZE! >> "%LOGFILE%"
goto :eofendlocal
运行后效果: