福福不服 福福不服
深圳 --°
正在加载音乐 请稍候

MySQL数据库自动备份实战:从零到一的完整指南

前言

在日常运维工作中,数据库备份是最基础也最重要的任务。手动备份不仅效率低下,还容易遗忘。本文将完整记录我如何用PowerShell脚本实现MySQL数据库的自动备份,并推送通知到企业微信群的全过程。

适用场景:Windows服务器 + Docker部署的MySQL + 企业微信机器人通知

一、需求分析

在开始编写脚本之前,先明确需求:

  1. 备份目标:每天凌晨2点自动备份MySQL数据库

  2. 备份内容:除dbsyncer_log外的所有业务数据库

  3. 存储策略:保留最近7天的备份文件,自动清理旧备份

  4. 通知机制:备份完成后推送成功/失败消息到企业微信群

  5. 运行环境:Windows任务计划程序定时执行,无人值守

二、技术选型

需求

技术方案

脚本语言

PowerShell(原生支持,编码处理方便)

备份工具

mysqldump(MySQL官方工具)

定时执行

Windows任务计划程序(taskschd.msc)

通知推送

企业微信机器人Webhook

日志记录

文本日志文件

三、环境准备

3.1 查看当前数据库列表

首先确认需要备份的数据库:

bash

docker exec mysql mysql -uroot -p密码 -e "SHOW DATABASES;"

排除策略

  • 排除系统库:information_schemaperformance_schemasys

  • 排除日志库:dbsyncer_log

  • 保留 mysql 库(用户权限信息)

3.2 企业微信机器人配置

  1. 在企业微信群中添加机器人

  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 参数,所以需要:

  1. 先查询所有数据库

  2. 在脚本中过滤排除列表

  3. --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. 备份参数说明

参数

说明

--single-transaction

使用事务保证一致性,适用于InnoDB

--quick

逐行导出,减少内存占用

--lock-tables=false

不锁表,避免影响业务

--databases

指定要备份的数据库列表

五、配置定时任务

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

---
备份脚本自动发送
image-p3sj.png