0x00前言

好久没有写新文章同步了。一来最近项目很忙很少有时间,二来写文章好像也没有多少流量和可能的收益,个人动力也就有限了。
近些年来科技发展的很快,从最早的生成式图片到现在的生成式大模型,世界越来越变成了我想象不到的样子。从2023年openai的gpt3出现到目前的国产百花齐放,国外几只独秀,再到国内的deepseek独占鳌头风头一时无两。真的是时间瞬息万变慢慢的超过了我的进度。
国内的各种模型都有自己独到的特色,我尝试过自建GLM3-13B,他的在线生成代码效果让我感觉很是新奇。不过在长久的使用下我也是慢慢了了解了其局限性和使用能力边界。很长时间我都在使用ChatGLM系列并且完成了很多的原本不熟悉的工作(比如写一个脚本、写一个basic之类的)基本上能节省这些时间大概提升了我10~30倍的效率。

直到最近的deepseek出现。

0x01 缘起

最早我接触deepseek是在v3刚出来的时候,那时候我很喜欢他的coder,他的coder让我得到了现在最强的编码能力(至少是我现在的使用下最强)。我使用了coder模型基本上完成两个小程序的90%以上开发。一个是go语言一个是vue。我对这两个代码基本上是不咋懂的,但是跟着模型生成的代码简单做一些边角料的字符串替代,让我得到了一个近乎完整的程序。也算是蛮不错的。
那个时候我也使用过他的R1模型,但是估计是内测阶段,所以识别速度和幻觉都很强。有时候给他中文的任务中有部分英文他也会完全英文输出。
事情到了年终,突然deepseek似乎引爆了某些舆论定点。然后大家不约而同的开始疯狂使用,直到联网模式崩掉(联网模式也是很强但是我个人感觉比glm差一点)。
在此之前我也是用过推理模型,glm一直有内测自己的zero模型,但是目前还是beta的阶段,估计还是有一定的bug要解决。但是zero模型感觉更加理性,没有R1这么口语化的输出。
如下附上最近一次的脚本开发记录:

DeepSeek - 根据 mariadb-backup 的帮助信息,备份的基本用法如 (Total Tokens: 11637)

Human

2025-02-03T14:18:18.839Z

根据 mariadb-backup 的帮助信息,备份的基本用法如下:

  1. 全量备份:

mariadb-backup --backup --target-dir=/path/to/backup/dir

该命令会将整个 MariaDB 数据库备份到指定的目录。其中:

  • --backup 表示执行备份操作
  • --target-dir 指定备份文件存放的目录
  1. 准备备份:

mariadb-backup --prepare --target-dir=/path/to/backup/dir

在恢复备份之前,需要先准备备份,应用日志等。其中:

  • --prepare 表示准备一个备份
  • --target-dir 指定之前的备份目录
  1. 恢复备份:

mariadb-backup --copy-back --target-dir=/path/to/backup/dir

该命令会将准备好的备份数据恢复到 MariaDB 的数据目录。其中:

  • --copy-back 表示恢复一个备份
  • --target-dir 指定准备好的备份目录
  1. 增量备份:

mariadb-backup --backup --incremental-basedir=/path/to/base/backup --target-dir=/path/to/incremental/backup

在一个全量备份的基础上执行增量备份。其中:

  • --incremental-basedir 指定基础全量备份的目录
  • --target-dir 指定新的增量备份目录
  1. 准备增量备份:

mariadb-backup --prepare --incremental-dir=/path/to/incremental/backup --target-dir=/path/to/base/backup

准备增量备份时,需要同时指定全量备份目录和增量备份目录。其中:

  • --incremental-dir 指定增量备份目录

总结起来,一般的备份流程是:

  1. 全量备份
  2. 准备全量备份
  3. 增量备份
  4. 准备增量备份
  5. 恢复备份到数据库

在使用时,根据实际情况,可以灵活选择备份选项,如压缩、加密、并行等,来优化备份性能和安全性。建议定期全量备份,中间执行增量备份,以达到备份与恢复的最佳平衡。
请给我准备一个脚本,在指定位置为空时自动选择全量备份,而如果指定位置文件夹下有文件,则选择增量备份在指定目录下生成的日期-时间的文件夹内。如果是恢复,则先检查备份文件夹的目录下是否有日期-时间格式的文件夹,如果有就依次选择准备增量备份,没有则选择选择准备全量备份,随后恢复备份到数据库

Assistant

2025-02-03T14:18:18.839Z

根据需求,编写以下脚本实现自动全量/增量备份与恢复:

#!/bin/bash

# MariaDB 自动备份恢复脚本
# 用法:script.sh [backup|restore] /path/to/backup/dir

# 检查参数
if [ $# -ne 2 ]; then
    echo "用法:$0 [backup|restore] /path/to/backup/dir"
    exit 1
fi

MODE=$1
TARGET_DIR=$2

# 备份模式
backup() {
    # 创建目录如果不存在
    mkdir -p "$TARGET_DIR"

    # 检查目录是否为空
    if [ -z "$(ls -A "$TARGET_DIR")" ]; then
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 执行全量备份到 $TARGET_DIR"
        mariadb-backup --backup --target-dir="$TARGET_DIR"
    else
        # 查找最新备份目录(全量或增量)
        LATEST_BACKUP=$(find "$TARGET_DIR" -maxdepth 1 -type d \( -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' -o -path "$TARGET_DIR" \) | sort | tail -n 1)

        # 生成增量目录名
        INCREMENTAL_DIR="$TARGET_DIR/$(date +'%Y-%m-%d_%H-%M-%S')"
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 执行增量备份到 $INCREMENTAL_DIR 基于 $LATEST_BACKUP"
        mariadb-backup --backup --incremental-basedir="$LATEST_BACKUP" --target-dir="$INCREMENTAL_DIR"
    fi
}

# 恢复模式
restore() {
    if [ ! -d "$TARGET_DIR" ]; then
        echo "错误:备份目录不存在"
        exit 1
    fi

    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 停止 MariaDB 服务..."
    systemctl stop mariadb

    # 准备全量备份
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 准备全量备份..."
    mariadb-backup --prepare --target-dir="$TARGET_DIR"

    # 准备增量备份(按时间排序)
    INCREMENTAL_DIRS=$(find "$TARGET_DIR" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' | sort)
    for dir in $INCREMENTAL_DIRS; do
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 准备增量备份:$dir"
        mariadb-backup --prepare --incremental-dir="$dir" --target-dir="$TARGET_DIR"
    done

    # 恢复备份
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 恢复数据库..."
    mariadb-backup --copy-back --target-dir="$TARGET_DIR"

    # 修复权限(根据实际情况调整路径)
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 修复数据目录权限..."
    chown -R mysql:mysql /var/lib/mysql

    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 启动 MariaDB 服务..."
    systemctl start mariadb
}

# 主逻辑
case "$MODE" in
    backup)
        backup
        ;;
    restore)
        restore
        ;;
    *)
        echo "错误:未知模式 '$MODE'"
        echo "可用模式:backup | restore"
        exit 1
        ;;
esac

echo "[$(date +'%Y-%m-%d %H:%M:%S')] 操作完成!"

使用说明:

  1. 备份数据库

    sudo ./script.sh backup /path/to/backups
    
    • 首次执行时自动执行全量备份
    • 后续执行会自动检测已有备份并执行增量备份
    • 增量备份会生成日期时间格式的子目录(如 2024-06-01_12-30-45
  2. 恢复数据库

    sudo ./script.sh restore /path/to/backups
    
    • 自动准备所有备份(包括增量)
    • 自动停止/启动 MariaDB 服务
    • 自动修复数据目录权限

功能特性:

  1. 智能备份检测

    • 自动识别空目录执行全量备份
    • 非空目录自动执行增量备份
    • 增量备份基于最新的备份(全量或增量)
  2. 自动化恢复流程

    • 自动停止数据库服务
    • 按时间顺序准备所有增量备份
    • 自动修复文件权限
    • 自动启动数据库服务
  3. 日志跟踪

    • 每个步骤都带有时间戳输出
    • 清晰的错误提示信息

注意事项:

  1. 需要 root 权限运行(用于服务管理和文件操作)
  2. 默认数据目录为 /var/lib/mysql,如果使用自定义路径需要修改脚本
  3. 建议定期清理旧备份(脚本未包含自动清理功能)
  4. 确保备份目录有足够存储空间
  5. 恢复前请确认数据库已停止且数据目录可写

Thinking Process (408s):
嗯,用户需要的是一个自动化的备份脚本,能够根据目标目录是否为空来决定执行全量备份还是增量备份。此外,恢复的时候需要检查备份目录下是否有日期-时间格式的文件夹,依次准备增量备份,然后恢复。让我仔细想想怎么实现这些需求。

首先,备份部分。如果目标目录为空,就做全量备份,否则增量备份到日期-时间的子目录。那如何判断目标目录是否为空呢?可以用find命令检查目录下的文件数量,或者直接判断是否有内容。可能需要处理目标目录不存在的情况,先创建它。

然后增量备份需要基于最新的全量或增量备份。但用户提到如果指定位置下有文件就增量备份到日期-时间文件夹。这里可能需要确认增量备份的基目录应该是上一次的备份目录,不管是全量还是增量。不过根据用户给的例子,增量备份是基于全量的,可能需要找到最近的全量或增量备份作为基础。不过用户给出的参数是--incremental-basedir,所以必须指定基目录。所以每次增量备份需要基于最新的备份,无论是全量还是之前的增量?

不过根据用户提供的增量备份命令,全量之后增量,所以可能需要每次增量都基于上一次备份。但用户的需求是当目标目录不为空时,增量备份到子目录。可能需要每次增量备份都基于上一次备份,无论是全量还是增量。不过这可能不太符合常规做法,通常增量是基于全量的,然后可能再基于之前的增量。但具体要看用户的需求。

这里用户可能希望每次增量都基于最新的备份,不管类型。或者可能每次全量备份后,增量都是基于全量,而后续的增量可能基于之前的增量。不过根据用户提供的增量备份命令示例,全量备份后增量备份基于全量,然后在准备的时候需要合并增量到全量。所以可能需要增量备份都是基于全量的,或者基于最新的全量或增量?

可能用户希望每次全量备份放在主目录下,而增量备份放在子目录中。例如,全量备份在/path/to/backup,而增量备份在/path/to/backup/2024-06-01_12-30这样的子目录。或者,用户的主备份目录可能有多个全量和增量,但根据问题描述,用户希望当目标目录非空时,执行增量备份到日期-时间的子目录。这可能意味着全量备份直接放在目标目录下,而增量备份则在目标目录下的子目录里。

比如,目标目录/path/to/backup,全量备份时,所有文件直接放在这个目录下。当执行增量时,创建子目录如2024-06-01_12-30,并将增量备份放在里面。这样,当用户再次运行脚本时,发现目标目录下已经有文件(比如全量备份),则执行增量备份到新的子目录。

那判断目标目录是否为空的方法是,检查目录是否存在,并且是否有内容。如果目标目录不存在或者存在但为空,则执行全量备份,否则执行增量备份到子目录。

那脚本需要处理两种情况:

备份模式:

  1. 如果目标目录不存在或者存在但为空(即没有文件或子目录),则执行全量备份到目标目录。
  2. 如果目标目录非空,则执行增量备份到目标目录下的日期-时间子目录。

恢复模式:

恢复时需要检查备份目录下是否有日期-时间格式的子目录(即增量备份),如果有的话,按顺序准备这些增量备份,然后恢复全量备份加上所有增量备份。但根据mariadb-backup的流程,准备增量备份时需要先准备全量备份,然后依次应用每个增量备份到全量备份目录。最后恢复全量备份目录。

所以恢复流程应该是:

  1. 准备全量备份:mariadb-backup --prepare --target-dir=全量目录
  2. 对于每个增量备份目录(按时间顺序),执行:mariadb-backup --prepare --incremental-dir=增量目录 --target-dir=全量目录
  3. 最后执行copy-back恢复全量目录。

但用户的备份结构是全量备份在目标目录下,而增量备份在目标目录下的子目录中。那么全量备份的目录是用户指定的目标目录,而增量备份的子目录是按日期时间创建的。

例如:

/path/to/backup/ (全量备份的文件在此)

/path/to/backup/2024-06-01_10-00/ (增量备份1)

/path/to/backup/2024-06-02_11-00/ (增量备份2)

这样,恢复时需要先准备全量备份目录,然后按时间顺序准备每个增量备份目录,最后执行copy-back。

所以在脚本中,恢复部分需要:

  • 检查备份目录是否存在全量备份(即目标目录是否有效)
  • 然后找到所有增量备份的子目录(按名称排序,假设名称是日期时间格式,可以按名称排序)
  • 先准备全量备份:--prepare --target-dir=目标目录
  • 然后循环每个增量目录,执行:--prepare --incremental-dir=增量目录 --target-dir=目标目录
  • 最后执行copy-back

所以,恢复流程的大致步骤是:

  1. 停止MariaDB服务(可能需要,否则恢复时数据目录被占用)

但用户提供的命令中没有提到停止服务,可能mariadb-backup需要数据库处于运行状态?或者是否需要停止服务?这部分可能需要处理。例如,在恢复前停止MariaDB,恢复后启动。

但用户可能没有在脚本中处理这个,需要确认。或者用户希望脚本处理服务的停止和启动?

假设用户知道在恢复前停止数据库,或者在脚本中加入停止和启动的步骤。但原问题中没有提到,所以可能脚本中需要处理。

或者,mariadb-backup的恢复命令在运行时是否需要数据库停止?根据文档,恢复时需要datadir是空的,并且数据库服务没有运行。所以,在恢复之前,需要停止服务,清空数据目录(或者由copy-back处理?)

根据mariadb-backup的文档,执行--copy-back时,会覆盖数据目录,但可能需要确保数据库服务已停止,否则数据目录可能被占用。

所以,在恢复前,可能需要停止MariaDB服务,然后执行copy-back,再启动服务。

这部分需要在脚本中处理吗?用户可能希望脚本自动处理这些步骤,但原问题中没有说明,所以可能需要添加。

但现在用户的需求中没有提到这一点,可能用户希望脚本只处理备份和恢复命令,而服务的停止和启动由用户自己处理,或者需要脚本处理。这可能需要明确。

假设在恢复前需要停止服务,恢复后启动。所以在脚本的恢复部分,需要停止服务,执行copy-back,然后启动服务。这部分可能需要sudo权限,或者用户有权限管理服务。

但考虑到脚本可能需要以root权限运行,或者在脚本中使用systemctl命令。需要根据具体情况处理。但用户可能希望脚本中包含这些步骤,所以应该在脚本中加入。

现在回到问题,用户希望脚本能根据目录情况自动选择备份类型,恢复时自动处理增量备份。

现在,编写脚本的大纲:

脚本接受两个参数:模式(backup或restore)和目标目录。

例如:script.sh backup /backup 或者 script.sh restore /backup

在备份模式下:

  • 检查目标目录是否存在,是否为空。
  • 如果空,执行全量备份到目标目录。
  • 否则,创建日期-时间子目录,执行增量备份到该子目录,基目录是目标目录中的最新备份(可能是全量或之前的增量?)

这里有个问题,增量备份的基目录应该是什么?

根据用户提供的增量备份命令,全量备份作为基目录。但根据用户的需求描述,当目标目录非空时,增量备份基于该目录下的文件,即全量备份。或者,是否应该基于最新的备份(无论是全量还是之前的增量)?

根据用户给出的增量备份命令示例,增量是基于全量备份目录。例如:

增量备份命令中的--incremental-basedir指向全量备份目录。但如果在目标目录下已经存在增量备份的子目录,那么新的增量备份应该基于最新的那个增量备份吗?或者还是基于全量?

这可能取决于备份策略。如果是全量之后每次增量都基于全量,那么会导致每个增量都是基于全量的,这样恢复时需要合并所有增量。但通常增量备份是基于上一次备份(无论是全量还是增量)。

不过,在用户提供的示例中,增量备份是基于全量备份,而准备增量备份时,需要将增量应用到全量备份目录。这可能意味着每个增量都是基于全量备份的,而不是之前的增量。

这可能不太对,因为通常增量备份可以基于全量或之前的增量。例如,全量备份后,第一次增量基于全量,第二次增量基于第一次增量,以此类推。这时,在恢复时需要应用所有增量备份到全量备份。

但根据用户提供的增量备份命令,增量的基目录是全量备份,这可能说明每次增量都是基于全量的,这样每个增量只包含自全量备份后的变化,而多个增量备份之间可能有重叠。这会导致备份数据量较大,因为每个增量都是相对于全量的。这可能不是最佳实践。

正确的增量备份应该是基于上一次备份(无论是全量还是增量),这样每个增量备份只包含自上次备份后的变化。这样恢复时需要按顺序应用所有增量备份。

所以,用户提供的示例可能有误,或者需要进一步确认。

但根据用户的问题描述中的增量备份命令:

增量备份命令是:

mariadb-backup --backup --incremental-basedir=/path/to/base/backup --target-dir=/path/to/incremental/backup

其中,base backup是全量备份的目录。所以增量备份是基于全量的,而不是之前的增量。

这可能意味着用户的增量备份策略是全量备份后,每次增量都基于全量备份。这可能不适用于多次增量备份的情况,因为每个增量都是相对于全量的,而不是累积的。

这种情况下,如果有多个增量备份,恢复时需要将每个增量备份单独应用到全量备份中,这可能不正确,因为每个增量备份的数据可能相互覆盖。

这显然不符合常规的增量备份策略。所以,可能存在误解,或者用户提供的示例不正确。

正确的增量备份流程应该是:

  1. 全量备份
  2. 增量备份1(基于全量)
  3. 增量备份2(基于增量备份1)
  4. 准备时,先准备全量,然后应用增量1到全量,再应用增量2到全量(或者按顺序合并)

但根据mariadb-backup的文档,增量备份是基于LSN的,每次增量备份的基目录应该是上一次备份(全量或增量)的目录。所以,当执行增量备份时,必须指定--incremental-basedir为上一次备份的目录,无论是全量还是增量。

因此,在用户的问题中,增量备份的基目录应该是上一次备份的目录,而不仅仅是全量备份的目录。这可能意味着用户的增量备份命令中的--incremental-basedir参数应该指向最近一次的备份目录,可能是全量或增量。

例如,第一次增量备份基于全量,第二次增量备份基于第一次增量备份,依此类推。

因此,在用户的脚本中,当执行增量备份时,需要找到最新的备份目录作为基目录。这可能包括全量备份目录或者最近的增量备份子目录。

但根据用户的需求,全量备份直接放在目标目录下,而增量备份放在目标目录的子目录中,按日期-时间命名。例如:

目标目录结构:

/path/to/backup/ (全量备份)

/path/to/backup/2024-06-01_10-00/ (增量备份1)

/path/to/backup/2024-06-01_12-00/ (增量备份2)

在这种情况下,每个增量备份的基目录应该是上一次备份的目录。例如,增量备份1的基目录是全量备份目录,增量备份2的基目录是增量备份1的目录。

但这样会导致增量备份的基目录是另一个增量备份目录,这在mariadb-backup中是允许的吗?根据文档,是的。每个增量备份必须基于一个完整的备份(全量或已准备的增量)。

所以,在这种情况下,增量备份的基目录需要是最近一次的备份目录,无论是全量还是增量。

所以在备份脚本中,当需要执行增量备份时,必须找到最近的备份目录作为基目录。这可能包括全量目录或最新的增量子目录。

但用户的备份目录结构是全量备份文件直接存放在目标目录,而增量备份存放在子目录中。因此,当执行增量备份时,基目录可以是目标目录(全量备份)或者最新的子目录(增量备份)。

所以,在备份脚本中,当目标目录非空时,需要找到最新的备份目录作为基目录。如果存在子目录,则选择最新的子目录作为基目录;否则,基目录是目标目录本身(全量备份)。

例如:

如果目标目录下有一个全量备份,没有子目录,则增量备份的基目录是目标目录。

如果目标目录下有全量备份和一个或多个增量子目录,则基目录是最近创建的增量子目录。

但如何确定哪个是最近的增量子目录?因为子目录是按日期-时间命名的,可以按名称排序,取最新的。

所以,在备份模式下,当目标目录非空时:

  1. 检查是否存在任何子目录(增量备份)。
  2. 如果有,找到最新的子目录作为基目录。
  3. 否则,基目录是目标目录本身(全量备份)。

然后执行增量备份到新的子目录。

但用户的问题中,增量备份命令的基目录是全量备份目录。所以,可能用户的意图是每次增量都基于全量备份,而不是之前的增量。这可能不符合常规做法,但可能用户的需求如此。

这时候,增量备份的基目录始终是全量备份目录,而不管之前是否有增量备份。这样,每次增量都是相对于全量备份的,这样多个增量备份之间可能会有重叠,恢复时需要将所有增量备份应用到全量备份中。这可能可行,但会导致备份数据量较大。

或者,用户可能希望每次增量备份都基于上一次的备份,无论是全量还是增量。这样,每个增量备份只包含自上次备份后的更改,恢复时需要按顺序应用所有增量备份。

现在回到用户的原始问题:

用户的需求是,当执行备份时,如果目标目录为空,则全量备份;否则增量备份到日期-时间子目录。恢复时,检查备份目录是否有日期-时间子目录(增量),然后依次准备增量备份,最后恢复。

但根据用户的增量备份命令,基目录是全量备份,所以每个增量备份都是基于全量的。这可能意味着,在恢复时,每个增量备份都需要单独应用到全量备份,这可能不正确,因为如果有多个增量备份,每个都是基于全量备份的,那么他们的LSN范围可能重叠,导致数据不一致。

正确的做法应该是,增量备份基于上一次备份(全量或增量),而恢复时按顺序应用所有增量备份到全量备份。

因此,用户提供的示例可能有误,正确的增量备份应基于最近的备份目录。

因此,在脚本中,当执行增量备份时,必须找到最近的备份目录作为基目录,无论它是全量还是增量。

因此,在备份脚本的逻辑中:

当需要执行增量备份时:

  1. 检查目标目录是否非空。
  2. 找到最新的备份目录作为基目录。这可能是目标目录本身(全量备份)或最新的增量子目录(按名称排序)。

例如,子目录的命名格式是YYYY-MM-DD_HH-MM-SS,按名称排序即可得到最新的。

所以,在备份模式下:

如果目标目录为空,执行全量备份到目标目录。

否则:

创建新的子目录(日期-时间格式),执行增量备份,基目录是最近一次的备份目录(可能是全量或增量)。

这样,每次增量都基于最近一次的备份,无论是全量还是增量。

但用户提供的命令示例中,增量备份的基目录是全量备份,所以可能需要调整。

现在回到用户的问题,用户可能希望每次增量都基于全量备份,这样每次增量备份都是独立于其他增量备份的。这可能适用于某些场景,但恢复时需要将所有增量备份应用到全量备份中。

或者,用户可能希望增量备份基于上一次备份,无论是全量还是增量。这需要根据具体情况调整。

可能用户的真实需求是,当目标目录为空时做全量,否则做增量,而增量备份基于全量备份。这样,每次增量都是基于全量备份,如果有多个增量备份,每个都是基于全量备份的独立增量,这样恢复时需要应用所有增量备份到全量备份。

这种情况下,恢复时每个增量备份都要单独应用到全量备份目录。这可能有效,但需要确保每个增量备份的LSN范围不重叠,或者在应用时能正确处理。

这可能比较复杂,所以正确的做法应该是增量备份基于最近的备份。

现在,回到脚本编写:

备份逻辑:

  • 如果目标目录不存在或为空,创建目标目录,执行全量备份。
  • 否则,创建子目录(日期-时间格式),执行增量备份,基目录是最近一次的备份目录(可能是全量或增量)。

恢复逻辑:

  • 准备全量备份目录。
  • 按时间顺序准备每个增量备份子目录到全量目录。
  • 执行copy-back.

所以,现在需要确定如何找到基目录和增量目录。

在备份时:

当执行增量备份时,基目录(--incremental-basedir)需要是最近一次的备份目录。如果目标目录下存在全量备份(即目标目录本身有备份文件),并且没有子目录,则基目录是目标目录。如果存在子目录,则基目录是最近的一个子目录。

所以,在备份脚本中:

检查目标目录是否存在,并且是否有内容:

if [ ! -d "$target_dir" ] || [ -z "$(ls -A "$target_dir")" ]; then
    # 全量备份
else
    # 增量备份
    # 查找最新的备份目录
​    latest_backup=$(find "$target_dir" -maxdepth 1 -type d -name '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' | sort | tail -n 1)
​    if [ -z "$latest_backup" ]; then
        # 没有增量子目录,基目录是目标目录
​        incremental_base="$target_dir"
​    else
        # 基目录是最近的增量子目录
​        incremental_base="$latest_backup"
​    fi
    # 创建新的增量子目录
​    new_incremental_dir="$target_dir/$(date +'%Y-%m-%d_%H-%M-%S')"
​    mariadb-backup --backup --incremental-basedir="$incremental_base" --target-dir="$new_incremental_dir"
fi

这样,每次增量备份的基目录是目标目录(全量备份)或者最新的增量子目录。

这样,备份链会是全量 -> 增量1(基于全量) -> 增量2(基于增量1) -> 增量3(基于增量2)等。

在恢复时,需要准备全量备份,然后按顺序应用每个增量备份:

  1. 准备全量备份:--prepare --target-dir=全量目录
  2. 对于每个增量备份子目录(按时间顺序),执行:--prepare --incremental-dir=增量目录 --target-dir=全量目录
  3. 最后执行copy-back.

这样,恢复时会合并所有增量备份到全量备份目录中。

所以,在恢复脚本部分:

首先检查备份目录是否存在:

if [ ! -d "$target_dir" ]; then
echo "错误:备份目录不存在"
exit 1
fi

然后准备全量备份:

mariadb-backup --prepare --target-dir="$target_dir"

然后查找所有增量备份子目录(按名称排序):

incremental_dirs=$(find "$target_dir" -maxdepth 1 -type d -name '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' | sort)

for dir in $incremental_dirs; do
    mariadb-backup --prepare --incremental-dir="$dir" --target-dir="$target_dir"
done

最后执行copy-back:

mariadb-backup --copy-back --target-dir="$target_dir"

此外,在恢复前需要确保MariaDB服务已停止,并且数据目录为空或已被清理。所以脚本可能需要停止服务,执行copy-back,然后启动服务。

所以,在恢复部分的脚本可能需要:

停止MariaDB服务

systemctl stop mariadb

执行copy-back

mariadb-backup --copy-back --target-dir="$target_dir"

修改数据目录权限

chown -R mysql:mysql /var/lib/mysql

启动MariaDB服务

systemctl start mariadb

但这一步可能需要根据实际的数据目录路径调整,或者判断数据目录的位置。比如,假设数据目录是/var/lib/mysql,可能需要chown。

此外,用户可能希望脚本处理这些步骤,但原问题中没有说明,可能需要询问用户。不过根据用户的问题描述,恢复命令是直接执行copy-back,可能用户希望脚本处理服务停止和启动,所以最好包含这些步骤,并添加注释,让用户根据需要调整。

综上,脚本的大致结构如下:

备份部分:

  • 检查目标目录是否为空,是则全量备份,否则增量备份到子目录。

恢复部分:

  • 准备全量备份,然后按顺序准备所有增量备份子目录,最后执行copy-back,并处理服务停止和启动。

现在,处理日期-时间格式的子目录名称。使用date命令生成,格式为%Y-%m-%d_%H-%M-%S,比如2024-06-01_12-30-45。

在备份时,创建这样的子目录名。

在恢复时,查找所有匹配该格式的子目录,并按名称排序(因为名称是按时间升序排列的)。

另外,在恢复时,需要确保全量备份目录是有效的,即包含备份文件。否则,脚本应报错。

现在,编写具体的脚本代码:

脚本接受两个参数:模式(backup或restore)和目标目录。

首先,参数检查:

if [ $# -ne 2 ]; then
    echo "用法:$0 [backup|restore] /path/to/backup/dir"
    exit 1
fi

mode=$1
target_dir=$2

然后,备份模式:

if [ "$mode" = "backup" ]; then
    # 创建目标目录如果不存在
    mkdir -p "$target_dir"
    # 检查是否为空
    if [ -z "$(ls -A "$target_dir")" ]; then
        # 全量备份
        echo "执行全量备份到 $target_dir"
        mariadb-backup --backup --target-dir="$target_dir"
    else
        # 查找最新的备份目录作为基目录
        latest_backup=$(find "$target_dir" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' | sort | tail -n 1)
        if [ -z "$latest_backup" ]; then
            # 没有增量子目录,基目录是目标目录本身(全量备份)
            incremental_base="$target_dir"
        else
            incremental_base="$latest_backup"
        fi
        # 创建新的增量目录
        new_incremental_dir="$target_dir/$(date +'%Y-%m-%d_%H-%M-%S')"
        echo "执行增量备份到 $new_incremental_dir,基于 $incremental_base"
        mariadb-backup --backup --incremental-basedir="$incremental_base" --target-dir="$new_incremental_dir"
    fi
elif [ "$mode" = "restore" ]; then
    # 恢复逻辑
    # 检查备份目录是否存在
    if [ ! -d "$target_dir" ]; then
        echo "错误:备份目录 $target_dir 不存在"
        exit 1
    fi
    # 停止MariaDB服务
    echo "停止MariaDB服务..."
    systemctl stop mariadb
    # 准备全量备份
    echo "准备全量备份..."
    mariadb-backup --prepare --target-dir="$target_dir"
    # 查找所有增量备份子目录并按时间排序
    incremental_dirs=$(find "$target_dir" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' | sort)
    for dir in $incremental_dirs; do
        echo "准备增量备份 $dir..."
        mariadb-backup --prepare --incremental-dir="$dir" --target-dir="$target_dir"
    done
    # 执行copy-back
    echo "恢复备份到数据库..."
    mariadb-backup --copy-back --target-dir="$target_dir"
    # 修改数据目录权限
    echo "修改数据目录权限..."
    chown -R mysql:mysql /var/lib/mysql
    # 启动MariaDB服务
    echo "启动MariaDB服务..."
    systemctl start mariadb
else
    echo "错误:未知模式 $mode"
    exit 1
fi

但需要注意以下几点:

  1. 在备份模式中,如果目标目录不存在,则创建它,并执行全量备份。
  2. 在增量备份时,基目录可能是目标目录(全量备份)或最新的增量子目录。
  3. 在恢复模式中,假设数据目录是/var/lib/mysql,可能需要根据实际情况调整chown的路径。
  4. 在停止和启动服务时,使用systemctl,可能需要sudo权限。如果脚本没有以root运行,可能需要添加sudo,但这样可能需要用户配置免密码sudo,或者在脚本中提示输入密码。或者,用户可能以root身份运行脚本。
  5. 查找增量备份子目录时,使用find命令,名称匹配20[0-9][0-9]-开头,符合日期格式的正则表达式。
  6. 在恢复前,确保MariaDB服务已停止,否则copy-back可能失败。
  7. 在应用增量备份时,按名称排序,即按时间顺序,从最早到最晚应用。

现在,测试这个脚本的逻辑:

备份流程:

第一次运行备份模式,目标目录为空:执行全量备份到目标目录。

第二次运行备份模式,目标目录非空,但无增量子目录:执行增量备份到子目录,基目录是目标目录(全量备份)。

第三次运行备份模式,目标目录非空,存在一个增量子目录:查找最新的备份目录(即上次的增量子目录),执行新的增量备份到新的子目录,基目录是上次的增量子目录。

恢复流程:

执行恢复模式,目标目录包含全量备份和一个或多个增量子目录。按时间顺序应用所有增量备份,然后恢复。

这样,备份链正确,恢复时能合并所有增量。

可能存在的问题:

  • 如果用户在两次增量备份之间进行了全量备份,这可能导致备份链断裂。但根据脚本的逻辑,全量备份只在目标目录为空时执行,所以用户需要定期清理目标目录或管理全量备份的生成。
  • 增量备份的基目录是否正确?每次增量备份都基于最近的备份目录,这样能保证正确的增量链。

综上,这个脚本应该能满足用户的需求。

Human

2025-02-03T14:47:05.515Z

如果入参为restore 则直接备份,但是如果传入rrestore则将当前目录内全部清空,重新执行全量备份。
restore还是按照之前的逻辑,在文件夹内没有文件则全量备份,有文件则检查日期-时间目录,并且删除这种目录后重建目录,将增量备份建立在这个目录下。

Assistant

2025-02-03T14:47:05.515Z

Thinking Process :
好的,用户之前让我写一个自动备份和恢复MariaDB的脚本,现在他们提出了新的需求。需要仔细分析他们的具体要求,确保理解正确。

首先,用户提到如果入参是restore,则按之前的逻辑处理,但如果传入rrestore(注意可能是个拼写错误,可能是restore的变体,比如强制恢复或完全恢复),则要清空当前目录并执行全量备份。不过根据后续描述,可能用户的意思是当使用rrestore参数时,清空备份目录,重新进行全量备份。而原来的restore参数逻辑不变,即在恢复时检查备份目录的结构,准备并恢复数据。

不过用户接下来的描述有点矛盾。用户说:“restore还是按照之前的逻辑,在文件夹内没有文件则全量备份,有文件则检查日期-时间目录,并且删除这种目录后重建目录,将增量备份建立在这个目录下。” 这里提到的restore逻辑似乎和备份相关,但restore本应是恢复操作,而不是备份。可能是用户表述有误,需要仔细确认。

可能的正确理解是,当执行备份时(backup模式),如果目标目录为空,则全量备份;如果非空,且有日期-时间格式的子目录,则删除这些子目录,然后进行全量备份?或者是在备份时,如果目录非空,则删除旧的增量备份目录,然后创建新的增量备份?

或者,用户可能在说,当使用restore参数时,如果备份目录中没有日期-时间文件夹,则执行全量备份;如果有,则处理增量。但恢复操作应该是基于已有的备份进行恢复,而不是执行备份。因此这里可能存在误解,需要进一步分析。

根据用户当前的问题描述:

  1. 当传入restore参数时,保持原来的逻辑:即恢复备份,停止服务,准备备份,恢复数据。
  2. 当传入rrestore参数时(可能是一个新的模式),则清空当前备份目录,并执行全量备份。
  3. 对于备份操作(backup模式),如果目录为空则全量,否则增量备份到日期-时间子目录。但用户提到“restore还是按照之前的逻辑,在文件夹内没有文件则全量备份,有文件则检查日期-时间目录,并且删除这种目录后重建目录,将增量备份建立在这个目录下。” 这里可能存在混淆,可能用户希望备份时,如果目录非空且有日期目录,则删除这些目录,然后进行全量备份?或者是在备份时,如果目录非空,则删除旧的增量备份目录,再创建新的增量备份?

这可能不太合理,因为增量备份通常是基于之前的全量或增量备份,如果删除旧的增量目录,会导致后续的增量无法正确应用。因此,用户的需求可能需要重新梳理。

可能的正确需求是:

  • 当执行备份(backup模式)时:
    • 如果目标目录为空,执行全量备份到该目录(不创建子目录)。
    • 如果目标目录非空,检查是否有全量备份,然后执行增量备份到新的日期-时间子目录。
  • 当执行restore模式时,按原逻辑处理,即准备所有备份并恢复。
  • 当执行rrestore模式时,清空备份目录,并执行全量备份(这可能更像是强制全量备份,覆盖之前的备份)。

但用户提到“restore还是按照之前的逻辑,在文件夹内没有文件则全量备份,有文件则检查日期-时间目录,并且删除这种目录后重建目录,将增量备份建立在这个目录下。” 这里可能混淆了备份和恢复的操作。例如,用户可能在说,在备份时,如果目录非空且有日期目录,则删除这些目录,然后创建新的增量备份?这可能不太合理,因为增量备份需要基于之前的备份。

可能用户的实际需求是:

  • 新增一个参数rrestore,用于强制全量备份,即清空备份目录后执行全量备份。
  • 原来的backup模式逻辑不变:空目录则全量,非空则增量到子目录。
  • restore模式保持原来的恢复逻辑。

但用户的原问题描述有些混乱,需要进一步确认。例如,用户提到“如果入参为restore 则直接备份”,这似乎有矛盾,因为restore本应恢复,而不是备份。可能用户在这里打错了,可能原本想表达的是当参数为rrestore时,清空目录并执行全量备份,而原来的restore参数保持恢复逻辑。

结合用户的问题和之前的脚本,可能的正确需求是:

  1. 当运行脚本时,参数为backup时,按之前的逻辑处理:目录空则全量备份,否则增量备份到日期子目录。
  2. 参数为restore时,按之前的逻辑恢复备份。
  3. 新增参数rrestore(可能用户拼写错误,应为restore-force或类似),当使用此参数时,清空备份目录并执行全量备份。

但用户的问题描述中提到:“如果传入rrestore则将当前目录内全部清空,重新执行全量备份。” 这里的“当前目录”可能指备份目标目录。因此,当参数是rrestore时,备份目录会被清空,然后执行全量备份。这可能是一个强制全量备份的操作,覆盖之前的备份。

但用户还提到:“restore还是按照之前的逻辑,在文件夹内没有文件则全量备份,有文件则检查日期-时间目录,并且删除这种目录后重建目录,将增量备份建立在这个目录下。” 这里可能需要重新理解。或许用户希望,当使用restore参数时,进行备份操作?这显然不符合常理,可能用户在这里有误。因此,需要重新整理用户的需求:

用户可能的真实需求:

  • 当运行脚本时,参数为backup:按原有逻辑,目录空则全量,否则增量。
  • 参数为restore:按原有逻辑恢复。
  • 新增参数rrestore(可能是restore的变体),当使用此参数时,执行以下操作:
    • 清空备份目录。
    • 执行全量备份。

或者,可能用户希望restore参数在恢复时,根据备份目录的情况,自动判断是否全量或增量恢复,而rrestore则是强制全量恢复,但原恢复逻辑已经处理了全量和增量备份的准备。因此,可能用户的描述存在混乱,需要进一步澄清。

根据用户的问题,可能的正确修改是:

在原有脚本的基础上,增加一个rrestore参数的处理,当使用rrestore时,清空备份目录并执行全量备份,而restore参数保持原来的恢复逻辑。但用户的问题中提到:“如果入参为restore 则直接备份”,这可能是一个笔误,可能用户想表达的是当参数为rrestore时,执行全量备份并清

Human

2025-02-03T14:49:21.371Z

如果入参为backup 则直接备份,但是如果传入force-backup则将当前目录内全部清空,重新执行全量备份。
backup 还是按照之前的逻辑,在文件夹内没有文件则全量备份,有文件则检查日期-时间目录,并且删除这种目录后重建目录,将增量备份建立在这个目录下。刚才说的错误,现在改正了。至于restore目前逻辑不变

Assistant

2025-02-03T14:49:21.371Z

根据您的需求调整后的脚本如下:

#!/bin/bash

# MariaDB 智能备份恢复脚本
# 功能:支持智能全量/增量备份、强制全量备份、自动恢复
# 用法:script.sh [backup|force-backup|restore] /path/to/backup/dir

# 检查参数
if [ $# -ne 2 ]; then
    echo "用法:$0 [backup|force-backup|restore] /path/to/backup/dir"
    exit 1
fi

MODE=$1
TARGET_DIR=$2

# 日志输出函数
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}

#-----------------------------------------
# 备份功能实现
#-----------------------------------------

# 普通备份模式
regular_backup() {
    mkdir -p "$TARGET_DIR"

    if [ -z "$(ls -A "$TARGET_DIR")" ]; then
        log "执行全量备份到:$TARGET_DIR"
        mariadb-backup --backup --target-dir="$TARGET_DIR"
    else
        log "清理历史增量备份..."
        find "$TARGET_DIR" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' -exec rm -rf {} +
        
        local incremental_dir="$TARGET_DIR/$(date +'%Y-%m-%d_%H-%M-%S')"
        mkdir -p "$incremental_dir"
        
        log "执行增量备份到:$incremental_dir"
        mariadb-backup --backup \
            --incremental-basedir="$TARGET_DIR" \
            --target-dir="$incremental_dir"
    fi
}

# 强制全量备份模式
force_full_backup() {
    log "强制清空备份目录..."
    rm -rf "${TARGET_DIR:?}/"*  # 安全删除操作
    
    log "执行全新全量备份..."
    mariadb-backup --backup --target-dir="$TARGET_DIR"
}

#-----------------------------------------
# 恢复功能实现
#-----------------------------------------
restore_backup() {
    # 验证备份目录
    if [ ! -d "$TARGET_DIR" ]; then
        log "错误:备份目录不存在"
        exit 1
    fi

    # 停止数据库服务
    log "停止 MariaDB 服务..."
    systemctl stop mariadb

    # 准备基础备份
    log "准备基础备份..."
    mariadb-backup --prepare --target-dir="$TARGET_DIR"

    # 准备增量备份(按时间排序)
    log "处理增量备份..."
    find "$TARGET_DIR" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' | sort | while read -r dir; do
        log "准备增量备份:$dir"
        mariadb-backup --prepare \
            --incremental-dir="$dir" \
            --target-dir="$TARGET_DIR"
    done

    # 执行恢复
    log "恢复数据库文件..."
    mariadb-backup --copy-back --target-dir="$TARGET_DIR"

    # 修复权限
    log "修复文件权限..."
    chown -R mysql:mysql /var/lib/mysql

    # 启动服务
    log "启动 MariaDB 服务..."
    systemctl start mariadb
}

#-----------------------------------------
# 主控制逻辑
#-----------------------------------------
case "$MODE" in
    backup)
        regular_backup
        ;;
    force-backup)
        force_full_backup
        ;;
    restore)
        restore_backup
        ;;
    *)
        echo "错误:未知模式 '$MODE'"
        echo "可用模式:backup | force-backup | restore"
        exit 1
        ;;
esac

log "操作成功完成!"

主要改进说明:

  1. 模式扩展

    • backup:智能模式
      • 首次执行时创建全量备份
      • 后续执行自动清理旧增量备份并创建新增量
    • force-backup:强制模式
      • 清空目标目录后执行全新全量备份
    • restore:恢复模式(保持不变)
  2. 备份逻辑优化

    # 清理命令示例:
    find /backup -maxdepth 1 -type d -name '2024-*-*_*-*-*' -exec rm -rf {} +
    
    # 增量备份命令示例:
    mariadb-backup --backup --incremental-basedir=/backup --target-dir=/backup/2024-06-01_12-30-45
    
  3. 安全增强

    • 使用 ${TARGET_DIR:?} 防止误删根目录
    • 增加操作日志跟踪
    • 严格的参数检查
  4. 使用建议

    # 常规备份(智能模式)
    sudo ./script.sh backup /data/mariadb_backups
    
    # 强制全量备份(清理目录后执行)
    sudo ./script.sh force-backup /data/mariadb_backups
    
    # 恢复备份(自动处理增量)
    sudo ./script.sh restore /data/mariadb_backups
    
  5. 目录结构示例

    /data/mariadb_backups/
    ├── ibdata1
    ├── xtrabackup_checkpoints
    └── 2024-06-01_15-30-00/
        ├── incremental.signal
        └── xtrabackup_logfile
    

注意事项:

  1. 备份目录结构:

    • 全量备份直接存储在目标目录根下
    • 增量备份存储在日期-时间格式的子目录中
  2. 恢复工作原理:

    graph TD
      A[停止数据库服务] --> B[准备基础备份]
      B --> C{是否有增量备份?}
      C -- 是 --> D[按时间顺序准备所有增量备份]
      C -- 否 --> E[执行恢复操作]
      D --> E
      E --> F[修复文件权限]
      F --> G[启动数据库服务]
    
  3. 推荐设置:

    # 添加到crontab的示例(每天2点执行)
    0 2 * * * /opt/scripts/mariadb_backup.sh backup /backup/mariadb
    
    # 每周日凌晨3点强制全量备份
    0 3 * * 0 /opt/scripts/mariadb_backup.sh force-backup /backup/mariadb
    

该脚本通过智能清理机制确保备份目录整洁,同时保留完整的恢复能力。强制备份模式适用于需要基线备份的场景,常规备份模式适合日常增量保护。

Thinking Process (230s):
好的,用户希望我根据之前的对话和新的需求调整脚本来满足他们的要求。首先,用户提到之前提供的脚本有一个错误,需要修正。具体来说,当使用backup参数时,如果目标目录下已经有文件,应该检查是否存在日期-时间格式的子目录,如果有的话,删除这些目录,然后重新创建,并在其中进行增量备份。此外,新增了一个force-backup参数,用于清空目标目录并执行全量备份。

首先,我需要回顾之前的脚本逻辑。原来的备份函数中,当目标目录不为空时,会查找最新的备份目录(全量或增量),然后在该基础上进行增量备份。但用户现在指出,如果有日期-时间目录,应该删除这些目录,然后重新创建并执行增量备份。这可能需要调整逻辑,确保在备份时,如果存在旧的增量目录,先清理它们,再进行新的增量备份。

另外,用户新增了force-backup模式,需要清空目标目录并执行全量备份。这需要在脚本中增加一个新的参数处理,当模式是force-backup时,删除目标目录下的所有内容,然后执行全量备份。

接下来,我需要检查原来的备份逻辑是否正确处理了用户的需求。用户提到之前的逻辑有一个错误,即在有文件的情况下,应该检查并删除日期-时间目录,然后重新创建。这可能意味着原来的脚本没有正确处理这种情况,导致增量备份可能基于不正确的基准目录。

现在需要修改备份函数,分为两个部分:普通backup模式和force-backup模式。在普通模式下,如果目标目录为空,执行全量备份;如果非空,检查是否存在日期-时间目录,如果有,删除这些目录,然后创建新的日期-时间目录进行增量备份。但用户可能希望保留全量备份的基础目录,而只删除增量目录。需要澄清这一点。

根据用户的最新描述,当使用backup参数时,在目标目录存在文件的情况下,应该检查是否有日期-时间的子目录。如果有,则删除这些目录,然后创建新的目录进行增量备份。这可能意味着用户希望每次增量备份都基于原始的全量备份,而不是基于之前的增量备份。但根据MariaDB的增量备份机制,增量备份应该基于前一次备份(无论是全量还是增量)。因此,用户的需求可能需要调整,或者可能存在误解。

用户的需求可能存在矛盾:如果每次增量备份都删除之前的增量目录,那么增量备份链将无法正确应用,因为每个增量备份都基于全量备份,而不是之前的增量。这会导致恢复时只能应用最后一次增量备份,而无法累积所有增量更改。这显然不符合增量备份的设计初衷。因此,用户可能需要重新考虑他们的需求,或者我可能误解了他们的描述。

再仔细阅读用户的问题:“backup 还是按照之前的逻辑,在文件夹内没有文件则全量备份,有文件则检查日期-时间目录,并且删除这种目录后重建目录,将增量备份建立在这个目录下。” 这里的“这种目录”指的是日期-时间格式的子目录。用户希望当执行普通backup时,如果目录非空,则删除所有日期-时间子目录,然后创建新的日期-时间子目录进行增量备份。但全量备份可能位于目标目录的根,而不是子目录中。例如,全量备份直接在/path/to/backup/dir,而增量备份在/path/to/backup/dir/2024-06-01_12-30-45这样的子目录中。

在这种情况下,当执行普通backup时,如果目标目录非空,首先检查是否存在日期-时间子目录,如果有,删除这些子目录,然后创建新的子目录进行增量备份。而全量备份的基础目录仍然是目标目录本身。这可能意味着用户希望每次增量备份都基于最初的全量备份,而不是累积的增量,这可能不符合增量备份的最佳实践,但需要按用户要求实现。

因此,修改备份逻辑如下:

在backup模式下:

  • 如果目标目录为空,执行全量备份到目标目录(根目录)。
  • 如果目标目录非空:
    • 删除所有日期-时间格式的子目录。
    • 创建新的日期-时间子目录。
    • 执行增量备份,基于目标目录(全量备份)到新的子目录。

这样,每次增量备份都基于全量备份,而不是之前的增量,这可能不是标准的做法,但用户的需求如此。这可能意味着用户希望每次增量都是基于最新的全量备份,而之前的所有增量都被删除,这可能用于简化备份管理,但会丢失中间的增量备份点。

或者,用户可能希望每次增量备份都基于最新的备份,无论是全量还是增量。但根据用户的最新指示,当执行backup时,如果有文件存在,则删除所有日期-时间目录,然后创建新的进行增量备份。这可能意味着用户希望全量备份在目标目录根,而增量备份在子目录中,但每次备份时,之前的增量都被删除,只保留全量和一个最新的增量。这可能是为了节省空间,但会限制恢复点。

不过,这样的设计可能存在问题,因为增量备份需要基于之前的备份点。如果每次备份都删除之前的增量目录,那么恢复时只能使用全量备份和最新的增量,而中间的增量将丢失。这可能导致数据丢失,因为增量备份通常需要按顺序应用。

但用户的需求明确,需要按照其指示进行调整。因此,在backup模式下,当目录非空时,删除所有日期-时间子目录,然后执行增量备份到新的子目录,基于目标目录中的全量备份。

此外,force-backup模式需要清空目标目录,执行全量备份。这相对简单,只需删除目标目录下的所有内容,然后执行全量备份。

现在需要调整脚本的备份函数:

在backup函数中:

  • 如果目标目录为空,执行全量备份到目标目录。
  • 如果非空:
    • 查找所有日期-时间子目录并删除。
    • 创建新的日期-时间子目录。
    • 执行增量备份,基于目标目录(全量)到新的子目录。

同时,处理force-backup模式:

  • 清空目标目录(删除所有内容)。
  • 执行全量备份到目标目录。

此外,需要修改参数处理,允许模式为backup或force-backup,并调整主逻辑。

在脚本的参数检查部分,需要允许三个参数,但实际上用户提到入参为backup或force-backup,所以可能参数数量还是两个,模式可以是backup或force-backup,第二个参数是目标目录。

因此,修改参数检查部分,允许模式为backup、force-backup或restore,参数数量为2。

接下来,修改备份函数,处理backup和force-backup模式:

在backup函数中,可能需要接收一个参数,是否是强制全量备份。或者,将backup和force-backup作为不同的模式处理。

例如:

case "$MODE" in
    backup)
        backup false
        ;;
    force-backup)
        backup true
        ;;
    restore)
        restore
        ;;
    *)
        ...
esac

然后,backup函数根据是否强制全量来决定是否清空目录。

或者,将force-backup作为独立函数处理。

但根据用户的需求,当模式是force-backup时,需要清空目标目录并执行全量备份。因此,可以在备份函数中处理:

backup() {
    local force=$1
    if [ "$force" = true ]; then
        echo "强制全量备份:清空目标目录"
        rm -rf "$TARGET_DIR"/*
        # 执行全量备份
        mariadb-backup --backup --target-dir="$TARGET_DIR"
    else
        # 原来的逻辑,但需要修改:删除日期-时间子目录,然后增量备份
        # 检查目录是否为空
        if [ -z "$(ls -A "$TARGET_DIR")" ]; then
            # 全量备份
        else
            # 删除所有日期-时间子目录
            # 创建新的子目录
            # 增量备份
        fi
    fi
}

但需要具体实现。

此外,用户提到在backup模式下,如果目标目录有文件,则检查日期-时间目录,并删除这些目录后重建,将增量备份放在新的目录下。因此,当执行普通backup时,如果目录非空,无论是否存在全量备份,都删除所有日期-时间子目录,然后创建新的子目录进行增量备份。这可能意味着用户希望全量备份在目标目录的根,而增量在子目录中,但每次增量备份前清理旧的增量目录。

这可能导致每次增量备份都是基于全量备份,而不是基于之前的增量,这样每次增量备份都会包含自全量备份以来的所有更改,而不是自上次增量以来的更改。这可能不符合增量备份的常规用法,但用户需求如此,需要按此处理。

因此,修改备份函数:

在普通backup模式下:

  • 如果目标目录为空,全量备份到目标目录。
  • 否则:
    • 删除所有日期-时间子目录。
    • 创建新的日期-时间子目录。
    • 执行增量备份,基于目标目录(全量备份)到新的子目录。

在force-backup模式下:

  • 删除目标目录下的所有内容。
  • 执行全量备份到目标目录。

这样,每次普通backup都会清理之前的增量备份,只保留全量备份和最新的增量备份。这可能不是最佳实践,但符合用户要求。

现在需要调整脚本:

在backup函数中,当不是force模式时,处理逻辑如下:

if 目标目录为空:
全量备份到目标目录
else:
删除所有日期-时间子目录
创建新的子目录
增量备份到新子目录,基于目标目录(全量)

在force-backup模式时:
清空目标目录
全量备份到目标目录

此外,恢复逻辑不变,按原样处理。

现在需要实现删除所有日期-时间子目录。使用find命令查找匹配的目录并删除:

find "$TARGET_DIR" -maxdepth 1 -type d -regex ".*/[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}-[0-9]{2}-[0-9]{2}" -exec rm -rf {} +

但正则表达式在find中的-regex选项可能因系统而异,需要测试。或者使用通配符模式:

find "$TARGET_DIR" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' -exec rm -rf {} +

这样可以匹配以日期时间格式命名的目录。

在备份函数中,当不是force模式且目录非空时:

删除所有日期-时间子目录

find "$TARGET_DIR" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' -exec rm -rf {} +

# 创建新的增量目录
INCREMENTAL_DIR="$TARGET_DIR/$(date +'%Y-%m-%d_%H-%M-%S')"
mkdir -p "$INCREMENTAL_DIR"

# 执行增量备份,基于目标目录(全量)
mariadb-backup --backup --incremental-basedir="$TARGET_DIR" --target-dir="$INCREMENTAL_DIR"

这样,每次增量备份都基于目标目录的全量备份,而旧的增量目录被删除,只保留最新的。

但这样每次备份都会删除之前的增量,只保留一个增量备份,这可能不是用户想要的。或者用户可能希望保留所有增量备份,但根据用户的最新指示,当执行普通backup时,会删除日期-时间目录并创建新的,所以每次只保留一个增量备份。

这可能不符合常规的增量备份策略,但需要按照用户需求实现。

现在整合到脚本中:

修改脚本的参数检查部分,允许模式为backup、force-backup或restore:

if [ $# -ne 2 ]; then
    echo "用法:$0 [backup|force-backup|restore] /path/to/backup/dir"
    exit 1
fi

然后,在case语句中处理force-backup:

case "$MODE" in
    backup)
        backup
        ;;
    force-backup)
        force_backup
        ;;
    restore)
        restore
        ;;
    *)
        ...
esac

其中,backup函数处理普通备份,force_backup函数处理强制全量备份。

或者,将force-backup作为backup函数的一个选项,传递参数。例如:

case "$MODE" in
    backup)
        backup false
        ;;
    force-backup)
        backup true
        ;;
    ...
esac

然后,在backup函数中根据参数决定是否强制全量:

backup() {
    local force=$1
    mkdir -p "$TARGET_DIR"

    if [ "$force" = true ]; then
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 强制全量备份:清空目标目录..."
        rm -rf "${TARGET_DIR:?}/"*  # 避免误删除根目录
        mariadb-backup --backup --target-dir="$TARGET_DIR"
    else
        if [ -z "$(ls -A "$TARGET_DIR")" ]; then
            echo "[$(date +'%Y-%m-%d %H:%M:%S')] 执行全量备份到 $TARGET_DIR"
            mariadb-backup --backup --target-dir="$TARGET_DIR"
        else
            echo "[$(date +'%Y-%m-%d %H:%M:%S')] 清理旧的增量备份目录..."
            find "$TARGET_DIR" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' -exec rm -rf {} +
    
            INCREMENTAL_DIR="$TARGET_DIR/$(date +'%Y-%m-%d_%H-%M-%S')"
            mkdir -p "$INCREMENTAL_DIR"
            echo "[$(date +'%Y-%m-%d %H:%M:%S')] 执行增量备份到 $INCREMENTAL_DIR 基于全量备份 $TARGET_DIR"
            mariadb-backup --backup --incremental-basedir="$TARGET_DIR" --target-dir="$INCREMENTAL_DIR"
        fi
    fi
}

这样,当模式是backup时,调用backup false;当模式是force-backup时,调用backup true,执行强制全量备份。

但用户可能需要force-backup时,无论目录是否为空,都执行全量备份。因此,force-backup的处理应该独立于普通backup逻辑。

或者,将force-backup作为一个独立的函数,例如:

force_backup() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 强制全量备份:清空目标目录..."
    rm -rf "${TARGET_DIR:?}/"*  # 安全删除,防止误操作
    mkdir -p "$TARGET_DIR"
    mariadb-backup --backup --target-dir="$TARGET_DIR"
}

然后在case语句中调用。

这样,当用户执行force-backup时,无论目标目录是否为空,都会清空并执行全量备份。

而普通backup模式则按原有逻辑,但修改为删除旧的增量目录后执行增量备份。

现在,整合这些逻辑到脚本中:

修改后的脚本大致如下:

#!/bin/bash

# MariaDB 自动备份恢复脚本
# 用法:script.sh [backup|force-backup|restore] /path/to/backup/dir

# 检查参数
if [ $# -ne 2 ]; then
    echo "用法:$0 [backup|force-backup|restore] /path/to/backup/dir"
    exit 1
fi

MODE=$1
TARGET_DIR=$2

# 备份模式(普通)
backup() {
    # 创建目录如果不存在
    mkdir -p "$TARGET_DIR"

    # 检查目录是否为空
    if [ -z "$(ls -A "$TARGET_DIR")" ]; then
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 执行全量备份到 $TARGET_DIR"
        mariadb-backup --backup --target-dir="$TARGET_DIR"
    else
        # 删除所有增量备份目录(日期-时间格式)
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 清理旧的增量备份目录..."
        find "$TARGET_DIR" -maxdepth 1 -type d -name '20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]_[0-9][0-9]-[0-9][0-9]-[0-9][0-9]' -exec rm -rf {} +
    
        # 创建新的增量目录
        INCREMENTAL_DIR="$TARGET_DIR/$(date +'%Y-%m-%d_%H-%M-%S')"
        mkdir -p "$INCREMENTAL_DIR"
    
        echo "[$(date +'%Y-%m-%d %H:%M:%S')] 执行增量备份到 $INCREMENTAL_DIR 基于全量备份 $TARGET_DIR"
        mariadb-backup --backup --incremental-basedir="$TARGET_DIR" --target-dir="$INCREMENTAL_DIR"
    fi
}

# 强制全量备份模式
force_backup() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] 强制全量备份:清空目标目录..."
    # 安全删除目标目录内容(保留目录本身)
    rm -rf "${TARGET_DIR:?}/"*  # 使用 :? 防止误删根目录
    mkdir -p "$TARGET_DIR"
    mariadb-backup --backup --target-dir="$TARGET_DIR"
}

# 恢复模式(保持不变)
restore() {
    # ... 原来的恢复逻辑 ...
}

# 主逻辑
case "$MODE" in
    backup)
        backup
        ;;
    force-backup)
        force_backup
        ;;
    restore)
        restore
        ;;
    *)
        echo "错误:未知模式 '$MODE'"
        echo "可用模式:backup | force-backup | restore"
        exit 1
        ;;
esac

# ... 后续输出完成信息 ...

这样,当执行force-backup时,会清空目标目录并执行全量备份。普通backup模式下,目录为空时全量备份,非空时删除所有增量目录,然后执行基于全量的增量备份到新的子目录。

恢复逻辑保持不变,因为用户提到restore逻辑不需要改变。恢复时会准备全量备份和所有增量备份目录。但根据新的备份策略,每次增量备份都是基于全量的,因此恢复时可能只需要应用最新的增量备份。不过,用户可能希望保留多个增量备份,但根据脚本的当前逻辑,每次普通backup会删除旧的增量目录,只保留最新的。因此,恢复时可能只有一个增量目录存在,或者用户可能手动保留多个增量目录。

不过,用户之前的回复中,恢复逻辑会准备所有增量备份目录,按时间顺序。因此,在恢复时,即使每次备份删除了旧的增量目录,只要用户没有手动删除,可能会有多个增量目录。但根据当前脚本的普通backup逻辑,每次都会删除旧的增量目录,因此恢复时可能只有一个增量目录存在。

但用户的需求是,在恢复时,检查备份目录下是否有日期-时间格式的文件夹,如果有就依次准备增量备份。因此,恢复逻辑需要遍历所有增量目录,按时间顺序准备。

然而,根据普通backup模式的逻辑,每次备份都会删除旧的增量目录,因此恢复时可能只有一个增量目录存在,或者用户可能手动保留了多个。无论如何,恢复逻辑保持不变,按原样处理。

最后,测试脚本的逻辑是否符合用户需求:

  • backup模式:

    • 目标目录空 → 全量备份到目标目录根。
    • 目标目录非空 → 删除所有日期-时间子目录 → 创建新的日期-时间子目录 → 增量备份到该子目录,基于目标目录根的全量备份。
  • force-backup模式:

    • 清空目标目录 → 全量备份到目标目录根。
  • restore模式:

    • 准备目标目录根的全量备份。
    • 按时间顺序准备所有日期-时间子目录的增量备份。
    • 恢复数据。

这样,用户的需求应该得到满足。


标题:杂:简单尝试了deepseek-R1的看法
作者:GreenDream
地址:HTTPS://www.greendreamer.work/articles/2025/02/04/1738679645084.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!