前言
在日常运维工作中,数据库备份是最基础也最重要的任务。手动备份不仅效率低下,还容易遗忘。本文将完整记录我如何用PowerShell脚本实现MySQL数据库的自动备份,并推送通知到企业微信群的全过程。
适用场景:Windows服务器 + Docker部署的MySQL + 企业微信机器人通知
一、需求分析
在开始编写脚本之前,先明确需求:
备份目标:每天凌晨2点自动备份MySQL数据库
备份内容:除
dbsyncer_log外的所有业务数据库存储策略:保留最近7天的备份文件,自动清理旧备份
通知机制:备份完成后推送成功/失败消息到企业微信群
运行环境:Windows任务计划程序定时执行,无人值守
二、技术选型
三、环境准备
3.1 查看当前数据库列表
首先确认需要备份的数据库:
bash
docker exec mysql mysql -uroot -p密码 -e "SHOW DATABASES;"排除策略:
排除系统库:
information_schema、performance_schema、sys排除日志库:
dbsyncer_log保留
mysql库(用户权限信息)
3.2 企业微信机器人配置
在企业微信群中添加机器人
获取Webhook地址:
text
https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的key
四、脚本编写
4.1 完整脚本代码
创建 mysql_backup.ps1,保存为 UTF-8 with BOM 编码:
# MySQL备份脚本 - PowerShell版本
# 保存为 UTF-8 with BOM 编码
# 用于任务计划程序自动执行,无交互
# ==================== 配置参数 ====================
$MYSQL_CONTAINER = "mysql"
$MYSQL_USER = "root"
$MYSQL_PASSWORD = "你的密码"
$BACKUP_DIR = "D:\backup\mysql_backup"
$SAVE_DAYS = 7
$WEBHOOK_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的key"
# 要排除的数据库列表
$EXCLUDE_DATABASES = @("information_schema", "performance_schema", "sys", "dbsyncer_log")
# 日志文件路径
$LOG_FILE = "D:\backup\backup.log"
# ==================== 日志函数 ====================
function Write-Log {
param([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] $Message"
Write-Host $logEntry
Add-Content -Path $LOG_FILE -Value $logEntry -Encoding UTF8
}
# ==================== 开始备份 ====================
Write-Log "========== 开始备份 =========="
# 创建备份目录
if (-not (Test-Path $BACKUP_DIR)) {
New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null
Write-Log "创建备份目录: $BACKUP_DIR"
}
# 生成备份文件名
$BACKUP_TIME = Get-Date -Format "yyyyMMdd_HHmm"
$BACKUP_FILE = Join-Path $BACKUP_DIR "mysql_$BACKUP_TIME.sql"
# ==================== 获取数据库列表 ====================
Write-Log "获取数据库列表..."
$cmd = "docker exec $MYSQL_CONTAINER mysql -u$MYSQL_USER --host=127.0.0.1 --protocol=tcp -p$MYSQL_PASSWORD -N -e `"SHOW DATABASES;`""
$output = Invoke-Expression -Command $cmd 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Log "❌ 获取数据库列表失败!错误: $output"
$BACKUP_STATUS = "❌ 失败"
$BACKUP_MSG = "获取数据库列表失败"
$BACKUP_FILE = ""
$backupDbs = @()
} else {
# 解析输出
$allDatabases = $output -split "`r`n" | Where-Object { $_.Trim() -ne "" }
Write-Log "找到 $($allDatabases.Count) 个数据库"
# 过滤数据库
$backupDbs = @()
foreach ($db in $allDatabases) {
$db = $db.Trim()
if ($db -and $EXCLUDE_DATABASES -notcontains $db) {
$backupDbs += $db
}
}
if ($backupDbs.Count -eq 0) {
Write-Log "❌ 没有找到需要备份的数据库!"
$BACKUP_STATUS = "❌ 失败"
$BACKUP_MSG = "没有找到需要备份的数据库(排除后为空)"
$BACKUP_FILE = ""
} else {
Write-Log "✅ 将备份 $($backupDbs.Count) 个数据库"
# ==================== 执行备份 ====================
Write-Log "开始备份数据库..."
$dbList = $backupDbs -join " "
$mysqldumpCmd = "docker exec -i $MYSQL_CONTAINER mysqldump -u$MYSQL_USER --host=127.0.0.1 --protocol=tcp -p$MYSQL_PASSWORD --databases $dbList --single-transaction --quick --lock-tables=false"
try {
& cmd /c $mysqldumpCmd ">" $BACKUP_FILE "2>&1"
if ($LASTEXITCODE -eq 0 -and (Test-Path $BACKUP_FILE) -and (Get-Item $BACKUP_FILE).Length -gt 0) {
$BACKUP_STATUS = "✅ 成功"
$fileSizeMB = [math]::Round((Get-Item $BACKUP_FILE).Length / 1MB, 2)
$BACKUP_MSG = "备份完成!"
Write-Log "✅ 备份完成: $BACKUP_FILE (大小: $fileSizeMB MB)"
# ==================== 清理旧备份 ====================
$oldFiles = Get-ChildItem -Path $BACKUP_DIR -Filter "*.sql" | Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$SAVE_DAYS) }
if ($oldFiles.Count -gt 0) {
Write-Log "清理旧备份文件 (保留最近 $SAVE_DAYS 天)..."
foreach ($file in $oldFiles) {
Remove-Item $file.FullName -Force
Write-Log " 已删除: $($file.Name)"
}
}
} else {
$BACKUP_STATUS = "❌ 失败"
$BACKUP_MSG = "备份失败!"
Write-Log "❌ 备份失败!"
}
} catch {
$BACKUP_STATUS = "❌ 失败"
$BACKUP_MSG = "备份异常: $($_.Exception.Message)"
Write-Log "❌ 备份异常: $($_.Exception.Message)"
}
}
}
# ==================== 获取文件大小 ====================
if ($BACKUP_FILE -and (Test-Path $BACKUP_FILE)) {
$FILE_SIZE = [math]::Round((Get-Item $BACKUP_FILE).Length / 1MB, 2)
} else {
$FILE_SIZE = 0
}
# ==================== 发送企业微信通知 ====================
Write-Log "发送通知到企业微信群..."
$content = @"
## 📦 MySQL数据库备份通知
> **状态**:$BACKUP_STATUS
> **文件**:$BACKUP_FILE
> **大小**:$FILE_SIZE MB
> **时间**:$BACKUP_TIME
---
<font color="comment">备份脚本自动发送</font>
"@
$body = @{
msgtype = "markdown"
markdown = @{
content = $content
}
}
$json = $body | ConvertTo-Json -Depth 10
$bytes = [System.Text.Encoding]::UTF8.GetBytes($json)
try {
$response = Invoke-RestMethod -Uri $WEBHOOK_URL -Method Post -Body $bytes -ContentType "application/json;charset=utf-8"
if ($response.errcode -eq 0) {
Write-Log "✅ 通知发送成功!"
} else {
Write-Log "❌ 通知发送失败: $($response.errmsg)"
}
} catch {
Write-Log "❌ 通知发送异常: $($_.Exception.Message)"
}
Write-Log "========== 备份完成 =========="4.2 关键技术点解析
1. 编码问题(最坑的地方)
PowerShell脚本必须保存为 UTF-8 with BOM 编码,否则企业微信消息中的中文会显示为问号。
💡 教训:CMD默认是GBK编码,但企业微信要求UTF-8。直接传递中文参数会乱码,解决方案是在PowerShell内部定义中文字符串,然后用
[System.Text.Encoding]::UTF8.GetBytes()转换。
2. 排除特定数据库
mysqldump 不支持 --exclude-database 参数,所以需要:
先查询所有数据库
在脚本中过滤排除列表
用
--databases参数指定要备份的数据库列表
# 获取所有数据库
$cmd = "docker exec mysql mysql -uroot -p密码 -N -e 'SHOW DATABASES;'"
# 过滤排除
$backupDbs = $allDatabases | Where-Object { $EXCLUDE_DATABASES -notcontains $_ }
# 执行备份
$mysqldumpCmd = "docker exec -i mysql mysqldump -uroot -p密码 --databases $dbList ..."3. 日志记录
所有操作都写入日志文件,方便排查问题:
powershell
function Write-Log {
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] $Message"
Add-Content -Path $LOG_FILE -Value $logEntry -Encoding UTF8
}4. 备份参数说明
五、配置定时任务
5.1 打开任务计划程序
text
Win + R → taskschd.msc → 回车5.2 创建任务
常规 选项卡:
名称:
MySQL数据库自动备份✅ 不管用户是否登录都要运行
✅ 使用最高权限运行
触发器 选项卡:
每天,开始时间:
02:00:00
操作 选项卡:
程序或脚本:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe添加参数:
-ExecutionPolicy Bypass -File "D:\你的脚本路径\mysql_backup.ps1"
条件 选项卡:
❌ 取消"只有在计算机使用交流电源时才启动此任务"
❌ 取消"只有当计算机空闲时才启动"
设置 选项卡:
✅ 允许按需运行任务
✅ 如果过了计划开始时间,立即启动任务
✅ 如果任务失败,重新启动:间隔1分钟,最多3次
✅ 如果任务运行时间超过1小时,停止任务
5.3 测试运行
右键任务 → 运行,检查:
备份文件是否生成
群通知是否收到
日志文件是否记录
六、最终效果
企业微信群通知
text
📦 MySQL数据库备份通知
> 状态:✅ 成功
> 文件:D:\backup\mysql_backup\mysql_20260902_0911.sql
> 大小:421.4 MB
> 时间:20260902_0911
---
备份脚本自动发送