【MySQL】从零搭建高性能、高可用的 MySQL 5.7 环境(附 XtraBackup 自动备份方案)

7 阅读4分钟

在生产环境中,稳定 + 高性能 + 可恢复 是数据库部署的三大黄金准则。本文将手把手带你从零开始,用二进制方式部署一个高度优化、安全可靠的 MySQL 5.7 实例,并集成 Percona XtraBackup 实现自动化全量/增量备份策略。

💡 为什么不用 yum/apt/docker?

二进制部署能精准控制版本、路径、依赖,避免系统包管理器带来的“黑盒”问题,更适合对稳定性要求极高的生产环境。

本方案在多个实际项目中运用多次,稳定高效。


🔧 一、准备工作:下载部署包

1. MySQL 社区版(5.7)

前往 MySQL Archive 下载 Generic Linux (glibc 2.12) x86_64 版本的 tar.gz 包(如 mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz)。

downloads.mysql.com/archives/co…

✅ 说明:选择 “Generic Linux” 表示这是通用二进制包,不依赖特定发行版的库,兼容性强;glibc 2.12 是 CentOS 7 的默认版本。

2. Percona XtraBackup(2.4)

⚠️ 注意:XtraBackup 2.4 已于 2023 年 11 月 EOL,仅适用于 MySQL 5.7。若你使用 MySQL 8.0,请升级到 XtraBackup 8.0。

选择 FullMinimal 版本(推荐 Full,包含 man 手册和调试符号)。

下载地址:Percona XtraBackup 2.4 Binary Tarball

✅ 作用:XtraBackup 是 Percona 提供的开源热备工具,支持 InnoDB 引擎的在线物理备份(无需锁表),是生产环境必备。

3. qpress(压缩工具)

XtraBackup 默认使用 qpress 进行压缩,需单独安装:

yum install qpress
或者
https://repo.percona.com/yum/release/7/RPMS/x86_64/qpress-11-3.el7.x86_64.rpm 下载

✅ 说明:qpress 是一种快速压缩算法,比 gzip 更适合数据库备份场景(支持并行压缩/解压)。


📁 二、创建标准化目录结构

良好的目录规划是运维效率的基础:

mkdir -p /mydata/{soft,script}

mkdir -p /mydata/{data,binlog,report/{inspection,logerr},backup/{xtrabackup/{full,fullx,incr},mylog/{errlog/{daily,weekly,monthly,yearly},slowlog/{daily,weekly,monthly,yearly},genlog}},dump}
目录用途说明
/mydata/soft存放所有原始安装包(便于回滚或重装)
/mydata/dataMySQL 数据目录(InnoDB 表空间、ibdata、frm 等)
/mydata/binlog二进制日志(用于主从复制、Point-in-Time 恢复)
/mydata/backup/xtrabackup/full当前有效的全量备份(用于增量基准)
/mydata/backup/xtrabackup/fullx历史归档全量(保留多个周期,防误删)
/mydata/backup/xtrabackup/incr增量备份(每天基于 full 生成)
/mydata/mylog/slowlog/...按时间维度归档慢查询日志,便于分析

✅ 设计原则:分离数据、日志、备份,避免 I/O 争抢;按时间分层,便于日志轮转和清理。


📦 三、解压部署包


mkdir -p /usr/local/{mysql,xtrabackup}
# 解压 MySQL
tar -xf /mydata/soft/mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz --strip-components=1 -C /usr/local/mysql

# 解压 XtraBackup
tar -xf /mydata/soft/percona-xtrabackup-2.4.27-Linux-x86_64.glibc2.12.tar.gz --strip-components=1 -C /usr/local/xtrabackup

# 安装 qpress(解压工具)
rpm -ivh qpress-11-3.el7.x86_64.rpm 

⚙️ 四、系统资源与内核参数优化

1. 用户资源限制(ulimit)

  • 交互式 Shell 生效
echo '
if [ $USER = "mysql" ]; then
 if [ $SHELL = "/bin/ksh" ]; then
 ulimit -u 16384
 ulimit -n 65536
 else
 ulimit -u 16384 -n 65536
 fi
fi' >> /etc/profile.d/root.sh
  • 当前会话立即生效
ulimit -Hu 16384 -Hn 65536
  • PAM 永久生效
echo '
mysql           soft    nproc   16384
mysql           hard    nproc   16384
mysql           soft    nofile  16384
mysql           hard    nofile  65536
mysql           soft    stack   16384
mysql           hard    stack   32768' >> /etc/security/limits.d/88-root-limits.conf
参数含义推荐值作用
nproc最大进程数16384防止因连接数过多导致 fork 失败
nofile最大打开文件数65536MySQL 需要大量 fd(表、连接、日志等)
stack栈大小(KB)16M/32M避免递归调用栈溢出

✅ 为什么需要? MySQL 在高并发下会创建大量线程和文件句柄,系统默认限制(通常 1024)极易成为瓶颈。

2. 内核参数调优

echo '
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10

vm.overcommit_memory = 2
vm.overcommit_ratio = 90

vm.swappiness = 0' >> /etc/sysctl.conf
sysctl -p
参数作用解释
vm.dirty_background_ratio=5当脏页占内存 5% 时,后台 flusher 线程开始写盘
vm.dirty_ratio=10脏页达 10% 时,应用 write() 会被阻塞直到刷盘完成(防止突发写爆内存)
vm.overcommit_memory=2严格检查内存分配(配合 overcommit_ratio)
vm.overcommit_ratio=90允许分配的内存 = swap + 90% * RAM(避免 OOM)
vm.swappiness=0强烈建议!尽量不用 swap,防止数据库性能抖动

✅ 这些参数对 I/O 密集型数据库 至关重要,能显著提升写入稳定性和响应速度。


👤 五、创建用户 & 权限配置

# 创建 mysql 用户(禁止登录)
getent passwd | grep mysql &> /dev/null || useradd -s /sbin/nologin mysql

# 授权目录
chown -R mysql.mysql /mydata /usr/local/mysql

# 添加 PATH(root 用户)
sed -i 's#PATH=.*#PATH=$PATH:$HOME/bin:/usr/local/mysql/bin:/usr/local/xtrabackup/bin#' ~root/.bash_profile

. ~root/.bash_profile

✅ useradd -s /sbin/nologin:创建无 shell 登录权限的系统用户,提升安全性。

chown -R mysql:mysql:确保 MySQL 进程有权限读写数据和程序目录。

✅ 修改 PATH:方便 root 用户直接使用 mysqlxtrabackup 等命令。


🔒 六、SELinux 配置(关键!)

SELinux 常导致“权限拒绝”却无明显报错。推荐两种方式:

方式一:禁用 SELinux(简单粗暴)

⚠️ 适用于测试环境或对安全要求不高的场景。

# 临时禁用
setenforce 0 

# 永久关闭
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config 

方式二:精细化授权(推荐生产环境)

# 先确认是否已安装了semanage
semanage -h

# 未安装的话先进行安装
yum install -y policycoreutils-python

# 标注数据目录
semanage fcontext -a -t mysqld_db_t "/mydata/data(/.*)?"
restorecon -R /mydata/data

# 标注 binlog 目录
semanage fcontext -a -t mysqld_log_t "/mydata/binlog(/.*)?"
restorecon -R /mydata/binlog

# 标注备份目录(按需选择类型)
semanage fcontext -a -t mysqld_db_t "/mydata/backup(/.*)?"
restorecon -R /mydata/backup
SELinux 类型用途
mysqld_db_tMySQL 数据文件(.ibd, .frm, ibdata1)
mysqld_log_t日志文件(binlog, error log, slow log)
mysqld_tmp_t临时文件(如 LOAD DATA 产生的 tmp 文件)

✅ 原理:SELinux 通过“类型强制”(Type Enforcement)控制进程对文件的访问。即使文件权限是 777,若类型不匹配,仍会被拒绝。

restorecon:根据策略重新打标签,使规则生效。

🔍 若遇到未知拒绝,用 ausearch -m avc | audit2why 分析建议类型。


📄 七、配置 my.cnf(性能核心!)

# 备份原文件
[ -e /etc/my.cnf ] && cp /etc/my.cnf /etc/my.cnf.`date "+%Y%m%d-%H%M%S"` 

注意,下面参数需要先计算好,非常重要!

💡 innodb_buffer_pool_size 计算公式:

物理内存 × 实例比例 × 0.7(一机一库则比例=1,一机两库则比例=1/2,以此类推)

得出结果后,将下方配置中对应的参数进行修改,不要用示例的参数!!!

echo "[client]
socket=/usr/local/mysql/mysql.sock

[mysqld]
##Base config
#字符集
character_set_server=UTF8
#存储引擎
default_storage_engine=InnoDB
#mysql运行用户
user=mysql
#监听端口
port=3306 
#mysql数据目录
datadir=/mydata/data
#最大连接数
max_connections=1000 
table_open_cache = 4000
table_definition_cache = 2000
thread_cache_size = 100

#innodb 缓冲池(RAM*70%,适用于一机一库)
innodb_buffer_pool_size=16G
innodb_buffer_pool_instances = 8

# 提升并发写入性能
innodb_flush_log_at_trx_commit = 1        # 保证 ACID(若可容忍少量丢失,可设为 2 提升性能)
innodb_flush_method = O_DIRECT            # 避免双缓冲
innodb_io_capacity = 2000                 # SSD 建议 2000~4000
innodb_io_capacity_max = 4000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
innodb_thread_concurrency = 0             # 由系统自动调度(推荐)

# 快速崩溃恢复
innodb_fast_shutdown = 0                  # 慢关机,但启动更快

# 自适应哈希索引(一般开启)
innodb_adaptive_hash_index = ON

#socket位置
socket=/usr/local/mysql/mysql.sock
#pid文件位置
pid_file=/usr/local/mysql/mysqld.pid
#错误日志位置
#log_error_verbosity=2
log_error=/mydata/data/mysql-error.log
#redo日志
innodb_log_file_size=2G
innodb_log_files_in_group=2
innodb_log_buffer_size=16M
#binlog日志
server_id=100
log_bin=/mydata/binlog/mysql-bin
binlog_format=row
expire_logs_days=31
log_bin_trust_function_creators=1
#slowlog
slow_query_log=on
long_query_time=10
slow_query_log_file=/mydata/data/mysql-slow.log
#跳过名称解析
skip_name_resolve=1
#开启计划任务
event_scheduler=1
#数据包的最大大小
max_allowed_packet=256M
#不区分大小写配置,以小写保存磁盘
lower_case_table_names=1
#sql_text的长度
performance_schema_max_sql_text_length=10240
#打开文件数限制
open_files_limit=65536
#日志时间格式
log_timestamps=SYSTEM
#sql模式
sql_mode="NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES"
#禁用timestamp字段update时更新时间戳
explicit_defaults_for_timestamp=on

##Replication config##
#gtid_mode=ON
#enforce_gtid_consistency=ON
#binlog_ignore_db=monitor
##slave config
#master_info_repository=table
#relay_log_info_repository=table
#relay_log=/mydata/binlog/relay-log
#relay_log_recovery=ON
#replicate_ignore_db=sys
#replicate_ignore_db=mysql
#replicate_ignore_db=information_schema
#replicate_ignore_db=performance_schema
#skip_slave_start
#slave_skip_errors=1062
#sql_slave_skip_counter=1

#查询日志(调试或审计)
#general_log=on
#general_log_file=/mydata/data/general.log

##Xtrabackup备份配置(请勿删除或修改)
[xtrabackup]
defaults_file=/etc/my.cnf
user=bkpuser
password=Bkpuser_2026
parallel=4
compress
compress-threads=4" > /etc/my.cnf
配置段参数名说明
[client]socket/usr/local/mysql/mysql.sock客户端连接本地 MySQL 时使用的 Unix socket 路径。
[mysqld]character_set_serverUTF8设置服务器默认字符集为 utf8(注意:MySQL 5.7 中 utf8 实际是 utf8mb3,不支持 4 字节 emoji)。
[mysqld]default_storage_engineInnoDB新建表默认使用 InnoDB 存储引擎(支持事务、行锁、外键)。
[mysqld]usermysqlmysqld 进程以 mysql 系统用户身份运行,提升安全性。
[mysqld]port3306MySQL 监听的 TCP 端口,默认为 3306。
[mysqld]datadir/mydata/dataMySQL 数据文件(如 ibdata1、.ibd、.frm 等)存储目录。
[mysqld]max_connections1000允许的最大并发连接数(含后台线程),超过将拒绝新连接。
[mysqld]table_open_cache4000表缓存大小,用于缓存已打开的表描述符,减少重复 open/close 开销。
[mysqld]table_definition_cache2000表定义缓存数量(如 .frm 文件或内部字典对象),避免频繁读取磁盘元数据。
[mysqld]thread_cache_size100线程缓存大小,复用线程以减少创建/销毁开销,提升短连接性能。
[mysqld]innodb_buffer_pool_size16GInnoDB 缓冲池大小,用于缓存数据和索引,是 InnoDB 性能最关键参数(此处设为 16GB)。
[mysqld]innodb_buffer_pool_instances8将缓冲池划分为 8 个独立实例,减少内部争用,提升高并发性能。
[mysqld]innodb_flush_log_at_trx_commit1每次事务提交都刷 redo log 到磁盘,保证 ACID(最安全,但 I/O 最高)。
[mysqld]innodb_flush_methodO_DIRECTInnoDB 数据文件 I/O 使用 O_DIRECT,绕过 OS 缓存,避免双缓冲(适合 SSD)。
[mysqld]innodb_io_capacity2000设备每秒可处理的 I/O 操作数(IOPS),用于控制后台刷新速率(SSD 推荐值)。
[mysqld]innodb_io_capacity_max4000后台任务(如脏页刷新)可突发达到的最大 IOPS。
[mysqld]innodb_read_io_threads8InnoDB 用于读操作的 I/O 线程数(默认 4,SSD 可适当增加)。
[mysqld]innodb_write_io_threads8InnoDB 用于写操作的 I/O 线程数(默认 4,SSD 可适当增加)。
[mysqld]innodb_thread_concurrency0InnoDB 内部线程并发数限制;0 表示不限制,由操作系统调度(MySQL 5.7 推荐值)。
[mysqld]innodb_fast_shutdown0关闭时执行完整 purge 和 merge,确保下次启动快速(值 0 = 慢速干净关机)。
[mysqld]innodb_adaptive_hash_indexON启用自适应哈希索引(AHI),对热点索引自动构建哈希索引,加速等值查询。
[mysqld]socket/usr/local/mysql/mysql.sockmysqld 监听的 Unix socket 文件路径,供本地客户端连接使用。
[mysqld]pid_file/usr/local/mysql/mysqld.pidmysqld 进程 ID 文件路径,用于管理脚本识别进程。
[mysqld]log_error/mydata/data/mysql-error.log错误日志文件路径,记录启动、运行、关闭过程中的关键信息。
[mysqld]innodb_log_file_size2G单个 redo log 文件大小(总大小 = 此值 × innodb_log_files_in_group)。
[mysqld]innodb_log_files_in_group2redo log 文件数量(通常为 2,总 redo 空间 = 2 × 2G = 4G)。
[mysqld]innodb_log_buffer_size16Mredo log 内存缓冲区大小,大事务可适当调高以减少磁盘写。
[mysqld]server_id100服务器唯一标识,在主从复制中必须全局唯一。
[mysqld]log_bin/mydata/binlog/mysql-bin启用二进制日志并指定基础路径和前缀(用于复制、PITR 恢复)。
[mysqld]binlog_formatrow二进制日志格式为 ROW,记录每一行变更,最安全且兼容性好。
[mysqld]expire_logs_days31自动清理 31 天前的 binlog 文件(MySQL 5.7 使用此参数,8.0 改为 binlog_expire_logs_seconds)。
[mysqld]log_bin_trust_function_creators1允许用户创建存储函数而不强制要求 DETERMINISTICREADS SQL DATA(方便开发,但有安全风险)。
[mysqld]slow_query_logon启用慢查询日志,记录执行时间超过阈值的 SQL。
[mysqld]long_query_time10慢查询阈值为 10 秒(单位:秒,可为小数如 0.1)。
[mysqld]slow_query_log_file/mydata/data/mysql-slow.log慢查询日志文件路径。
[mysqld]skip_name_resolve1跳过客户端主机名 DNS 反向解析,提升连接速度并避免 DNS 问题。
[mysqld]event_scheduler1启用事件调度器(可执行定时任务,等价于 ON)。
[mysqld]max_allowed_packet256M单个 SQL 语句或结果集允许的最大大小(影响大 BLOB/批量插入)。
[mysqld]lower_case_table_names1表名和数据库名以小写存储并比较(Windows/macOS 兼容模式,Linux 上需初始化时设定)。
[mysqld]performance_schema_max_sql_text_length10240Performance Schema 中记录的 SQL 文本最大长度(默认 1024,此处扩展至 10KB)。
[mysqld]open_files_limit65536设置 mysqld 可打开的最大文件描述符数(需操作系统 ulimit 支持)。
[mysqld]log_timestampsSYSTEM日志时间戳使用系统时区(而非 UTC),便于与系统日志对齐(MySQL 5.7.2+ 支持)。
[mysqld]sql_mode"NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES"SQL 模式:• STRICT_TRANS_TABLES:对事务表启用严格模式(非法值报错)• NO_ENGINE_SUBSTITUTION:建表时若指定引擎不可用则报错(不降级)。
[mysqld]explicit_defaults_for_timestampon禁用 TIMESTAMP 字段的隐式默认行为(如自动更新),需显式声明(MySQL 5.6+ 推荐开启)。
[xtrabackup]defaults_file/etc/my.cnfXtraBackup 读取此配置文件获取连接信息(非 mysqld 参数,仅 XtraBackup 使用)。
[xtrabackup]userbkpuserXtraBackup 连接 MySQL 所用的用户名。
[xtrabackup]passwordBkpuser_2026XtraBackup 用户密码
[xtrabackup]parallel4XtraBackup 并行备份线程数(加速备份过程)。
[xtrabackup]compress(无值)启用备份压缩(使用 zlib)。
[xtrabackup]compress-threads4压缩所用的线程数。

✅ [xtrabackup] 段:XtraBackup 会读取此段配置,实现免密、并行、压缩备份,注意修改对应的备份账号和密码。


🛠️ 八、初始化 & 启动服务

1. 初始化数据目录(无密码 root)

mysqld --initialize-insecure --user=mysql --datadir=/mydata/data 

✅ --initialize-insecure:创建空密码 root 账号(仅限内网或首次初始化使用,后续必须改密)。

✅ 若用 --initialize,会生成随机密码并打印到 error log。

2. 配置 systemd 服务(现代 Linux 必备)

cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld

chmod +x /etc/init.d/mysqld

sed -i "s#^basedir=.*#basedir=/usr/local/mysql#g" /etc/init.d/mysqld
sed -i "s#^datadir=.*#datadir=/mydata/data#g" /etc/init.d/mysqld
echo '[Unit]
Description=MySQL 5.7 Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target

[Install]
WantedBy=multi-user.target

[Service]
Type=forking
User=mysql
ExecStart=/etc/init.d/mysqld start
ExecReload=/etc/init.d/mysqld restart
ExecStop=/etc/init.d/mysqld stop

# Disable service start and stop timeout logic of systemd for mysqld service.
TimeoutSec=0

# Execute pre and post scripts as root
PermissionsStartOnly=true

PrivateTmp=false' > /usr/lib/systemd/system/mysqld.service

✅ Type=forking:表示服务会 fork 子进程,systemd 需等待主进程退出。

TimeoutSec=0:禁用超时,避免大实例启动慢被 kill。

PrivateTmp=false:允许使用系统 /tmp(某些插件依赖)。

3. 启动服务

# 重载配置并设置开机启动
systemctl daemon-reload && systemctl enable mysqld

# 启动mysql
systemctl start mysqld

🔐 九、安全加固

1. 设置 root 密码

mysql -u root --skip-password -e "alter user 'root'@'localhost' identified by 'Wenb1n_dev';"

# 验证是否成功
mysqladmin -uroot -pWenb1n_dev version

2. 启用密码策略插件

#先登陆到mysql中
mysql -uroot -pWenb1n_dev 

#执行sql
INSTALL PLUGIN validate_password SONAME 'validate_password.so';

✅ 启用后,创建用户时会强制校验密码强度(长度、数字、大小写、特殊字符等)。

3. 创建运维超级账号(锁定 root)

#创建运维超级帐号
CREATE USER 'u_admin'@'localhost' IDENTIFIED BY 'Wenb1n_dev';

#授权运维超级账号
GRANT ALL ON *.* TO 'u_admin'@'localhost' WITH GRANT OPTION;

#禁用root账号
ALTER USER 'root'@'localhost' ACCOUNT LOCK;

✅ 最佳实践:生产环境不应使用 root 操作,应创建专用运维账号并锁定 root。

4. 开放防火墙

firewall-cmd --permanent --add-port=3306/tcp && firewall-cmd --reload

✅ 仅开放必要端口,遵循最小开放原则。


🗃️ 十、创建业务 & 备份账号

-- 建用户(如已存在可跳过)
CREATE USER 'demo'@'%' IDENTIFIED BY 'Strong_P@ss';

-- 授予 salesdb 的全部权限(相当于除了 GRANT 以外的所有)
GRANT ALL PRIVILEGES ON salesdb.* TO 'demo'@'%';

-- 如果还想让他把权限转授别人,再加:
GRANT GRANT OPTION ON salesdb.* TO 'demo'@'%';

-- 创建备份账号
create user 'bkpuser'@'localhost' identified by 'Bkpuser_2026';grant reload,process,lock tables,replication client on *.* to 'bkpuser'@'localhost';

FLUSH PRIVILEGES;

✅ 备份账号权限说明:

  • RELOAD:执行 FLUSH TABLES WITH READ LOCK(FTWRL)
  • PROCESS:查看进程列表(用于监控)
  • LOCK TABLES:加表锁(MyISAM 备份需要)
  • REPLICATION CLIENT:获取 binlog 位置(用于 PITR)

🔄 十一、自动化备份脚本(XtraBackup)

将以下脚本保存为 /mydata/script/backup/xtrabackup_auto.sh,并添加 cron 任务:

# 每天凌晨 3 点执行
0 3 * * * /mydata/script/backup/xtrabackup_auto.sh >> /var/log/xtrabackup_cron.log2>&1

✅ 策略说明:

  • 周日:全量备份 → 存入 full/
  • 周一至六:增量备份 → 基于 full/ 存入 incr/YYYYMMDD/
  • 自动归档:旧全量移至 fullx/,保留 38 天
  • 自动清理:增量保留 31 天,至少保留最新 1 份

📌 脚本已内置:并发锁、空目录防护、有效性校验、日志记录。

首次备份手工执行全量备份
xtrabackup --backup --target-dir=/mydata/backup/xtrabackup/full
#!/bin/bash
# =============================================================================
# MySQL XtraBackup 自动备份脚本(全量 + 增量)
# 
# 功能说明:
#   - 每周日 03:00 执行全量备份
#   - 周一至周六 03:00 执行增量备份(基于最近一次有效全量)
#   - 自动清理过期备份(保留策略:增量31天,全量保留两个周期共38天以上)
#   - 防止空目录误删、并发执行、无效备份等问题
# 使用方式:通过 cron 调用,例如:
#   0 3 * * * /path/to/xtrabackup_auto.sh
# =============================================================================

# 加载 root 环境变量(确保 xtrabackup 命令可用)
source ~root/.bash_profile

# -----------------------------
# 1. 配置参数
# -----------------------------

# 备份周期(天):7天为一个完整周期(周日全量,其余增量)
BACKUP_CYCLE_DAYS=7

# 增量备份保留天数
INCREMENTAL_RETAIN_DAYS=31

# fullx 目录中全量备份保留时间 = 周期 + 保留天数(至少保留两个全量)
FULLX_RETAIN_DAYS=$((BACKUP_CYCLE_DAYS + INCREMENTAL_RETAIN_DAYS))  # 38天

# 当前星期几(0=周日, 1=周一, ..., 6=周六)
WEEKDAY=$(date +%w)

# 当前时间戳(用于日志)
CURRENT_TIME=$(date '+%Y-%m-%d %H:%M:%S')

# 备份目录定义
FULL_DIR="/mydata/backup/xtrabackup/full"        # 当前有效的全量备份目录
FULLX_DIR="/mydata/backup/xtrabackup/fullx"      # 归档的全量备份(历史)
INCR_DIR="/mydata/backup/xtrabackup/incr"        # 增量备份目录

# 今日增量子目录名(格式:YYYYMMDD)
INCR_SUBDIR=$(date '+%Y%m%d')

# 7天前的日期(用于归档旧全量,格式:YYYYMMDD)
ARCHIVE_DATE=$(date -d "-${BACKUP_CYCLE_DAYS} days" '+%Y%m%d')

# 日志文件路径(带时间戳)
LOG_FILE="/mydata/script/backup/log/xtrabackup_$(date '+%Y%m%d-%H%M%S').log"

# 锁文件(防止脚本并发执行)
LOCK_FILE="/tmp/xtrabackup.lock"

# -----------------------------
# 2. 工具函数
# -----------------------------

# 写入日志(带时间前缀)
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}

# 安全获取目录中最新子目录(避免空目录问题)
get_latest_subdir() {
    local dir="$1"
    if [ ! -d "$dir" ] || [ -z "$(ls -A "$dir")" ]; then
        echo ""  # 返回空表示无内容
        return
    fi
    # 按修改时间排序,取最新非隐藏目录
    find "$dir" -mindepth 1 -maxdepth 1 -type d ! -name ".*" -printf '%T@ %f\n' 2>/dev/null | \
        sort -n | tail -1 | cut -d' ' -f2
}

# 检查 XtraBackup 全量备份是否有效(存在关键文件)
is_valid_full_backup() {
    local dir="$1"
    [ -f "$dir/xtrabackup_checkpoints" ] && grep -q "full-backuped" "$dir/xtrabackup_checkpoints"
}

# -----------------------------
# 3. 初始化
# -----------------------------

# 创建必要目录
mkdir -p "$FULL_DIR" "$FULLX_DIR" "$INCR_DIR" "$(dirname "$LOG_FILE")"

# 初始化日志
log "========== 开始 XtraBackup 自动备份任务 =========="
log "当前时间: $CURRENT_TIME"
log "今天是星期: $WEEKDAY (0=周日)"

# 检查锁文件,防止并发
if [ -f "$LOCK_FILE" ]; then
    log "错误:检测到上一次备份仍在运行(锁文件存在),退出。"
    exit 1
fi

# 创建锁文件
trap 'rm -f "$LOCK_FILE"; log "脚本异常退出,已清理锁文件。"' EXIT
echo $$ > "$LOCK_FILE"

# -----------------------------
# 4. 清理过期备份
# -----------------------------

log "开始清理过期备份..."

# --- 清理增量备份(保留最近31天,但至少保留最新1个)---
LATEST_INCR=$(get_latest_subdir "$INCR_DIR")
if [ -n "$LATEST_INCR" ]; then
    log "最新增量备份目录: $LATEST_INCR"
    find "$INCR_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +$INCREMENTAL_RETAIN_DAYS ! -name "$LATEST_INCR" -exec rm -rf {} \; 2>>"$LOG_FILE"
    log "已清理超过 ${INCREMENTAL_RETAIN_DAYS} 天的增量备份(保留最新一份)。"
else
    log "增量备份目录为空,跳过清理。"
fi

# --- 清理归档全量备份(保留最近38天,但至少保留最新2个)---
LATEST_FULLX_1=$(get_latest_subdir "$FULLX_DIR")
LATEST_FULLX_2=$(find "$FULLX_DIR" -mindepth 1 -maxdepth 1 -type d ! -name ".*" -printf '%T@ %f\n' 2>/dev/null | \
                 sort -n | tail -2 | head -1 | cut -d' ' -f2)

if [ -n "$LATEST_FULLX_1" ]; then
    log "最新两个归档全量: $LATEST_FULLX_2, $LATEST_FULLX_1"
    # 构建 find 条件:排除最新两个
    FIND_CMD="find \"$FULLX_DIR\" -mindepth 1 -maxdepth 1 -type d -mtime +$FULLX_RETAIN_DAYS"
    [ -n "$LATEST_FULLX_1" ] && FIND_CMD="$FIND_CMD ! -name \"$LATEST_FULLX_1\""
    [ -n "$LATEST_FULLX_2" ] && FIND_CMD="$FIND_CMD ! -name \"$LATEST_FULLX_2\""
    eval "$FIND_CMD -exec rm -rf {} \;" 2>>"$LOG_FILE"
    log "已清理超过 ${FULLX_RETAIN_DAYS} 天的归档全量备份(保留最新两份)。"
else
    log "归档全量目录为空,跳过清理。"
fi

# -----------------------------
# 5. 执行备份
# -----------------------------

if [ "$WEEKDAY" -eq 0 ]; then
    # ========== 周日:执行全量备份 ==========
    log "今天是周日,执行全量备份。"

    # 如果 FULL_DIR 存在且有效,先归档到 FULLX_DIR
    if [ -d "$FULL_DIR" ] && is_valid_full_backup "$FULL_DIR"; then
        ARCHIVE_PATH="$FULLX_DIR/$ARCHIVE_DATE"
        log "将当前全量备份归档至: $ARCHIVE_PATH"
        mv "$FULL_DIR" "$ARCHIVE_PATH"
    elif [ -d "$FULL_DIR" ]; then
        log "警告:当前全量目录存在但无效,直接删除。"
        rm -rf "$FULL_DIR"
    fi

    # 创建新全量目录
    mkdir -p "$FULL_DIR"

    # 执行全量备份
    log "开始执行 XtraBackup 全量备份..."
    if xtrabackup --backup --target-dir="$FULL_DIR" >>"$LOG_FILE" 2>&1; then
        log "✅ 全量备份成功完成。"
    else
        log "❌ 全量备份失败!请检查日志。"
        exit 1
    fi

else
    # ========== 周一至周六:执行增量备份 ==========
    log "今天是工作日(星期 $WEEKDAY),尝试执行增量备份。"

    # 检查 FULL_DIR 是否存在且有效
    if [ ! -d "$FULL_DIR" ] || ! is_valid_full_backup "$FULL_DIR"; then
        log "错误:未找到有效的全量备份($FULL_DIR 不存在或无效),无法执行增量备份!"
        log "建议:请先手动执行一次全量备份,或等待周日自动全量。"
        exit 1
    fi

    # 创建今日增量目录
    TODAY_INCR_DIR="$INCR_DIR/$INCR_SUBDIR"
    mkdir -p "$TODAY_INCR_DIR"

    # 执行增量备份
    log "基于 $FULL_DIR 执行增量备份到 $TODAY_INCR_DIR ..."
    if xtrabackup --backup --target-dir="$TODAY_INCR_DIR" --incremental-basedir="$FULL_DIR" >>"$LOG_FILE" 2>&1; then
        log "✅ 增量备份成功完成。"
    else
        log "❌ 增量备份失败!请检查日志。"
        exit 1
    fi
fi

# -----------------------------
# 6. 结束
# -----------------------------

log "========== XtraBackup 备份任务结束 =========="

# 清理锁文件
rm -f "$LOCK_FILE"
trap - EXIT

exit 0

✅ 总结:这套方案的优势

特性说明
纯净部署无系统包依赖,版本可控
性能极致内核 + MySQL 参数双重优化
安全合规SELinux 支持、最小权限账号
灾备可靠XtraBackup 热备 + 自动化策略
运维友好标准化目录 + systemd 管理

关注我,获取更多 DBA 实战干货!

如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~ ❤️