在生产环境中,稳定 + 高性能 + 可恢复 是数据库部署的三大黄金准则。本文将手把手带你从零开始,用二进制方式部署一个高度优化、安全可靠的 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。
选择 Full 或 Minimal 版本(推荐 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/data | MySQL 数据目录(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 | 最大打开文件数 | 65536 | MySQL 需要大量 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 用户直接使用mysql、xtrabackup等命令。
🔒 六、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_t | MySQL 数据文件(.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_server | UTF8 | 设置服务器默认字符集为 utf8(注意:MySQL 5.7 中 utf8 实际是 utf8mb3,不支持 4 字节 emoji)。 |
[mysqld] | default_storage_engine | InnoDB | 新建表默认使用 InnoDB 存储引擎(支持事务、行锁、外键)。 |
[mysqld] | user | mysql | mysqld 进程以 mysql 系统用户身份运行,提升安全性。 |
[mysqld] | port | 3306 | MySQL 监听的 TCP 端口,默认为 3306。 |
[mysqld] | datadir | /mydata/data | MySQL 数据文件(如 ibdata1、.ibd、.frm 等)存储目录。 |
[mysqld] | max_connections | 1000 | 允许的最大并发连接数(含后台线程),超过将拒绝新连接。 |
[mysqld] | table_open_cache | 4000 | 表缓存大小,用于缓存已打开的表描述符,减少重复 open/close 开销。 |
[mysqld] | table_definition_cache | 2000 | 表定义缓存数量(如 .frm 文件或内部字典对象),避免频繁读取磁盘元数据。 |
[mysqld] | thread_cache_size | 100 | 线程缓存大小,复用线程以减少创建/销毁开销,提升短连接性能。 |
[mysqld] | innodb_buffer_pool_size | 16G | InnoDB 缓冲池大小,用于缓存数据和索引,是 InnoDB 性能最关键参数(此处设为 16GB)。 |
[mysqld] | innodb_buffer_pool_instances | 8 | 将缓冲池划分为 8 个独立实例,减少内部争用,提升高并发性能。 |
[mysqld] | innodb_flush_log_at_trx_commit | 1 | 每次事务提交都刷 redo log 到磁盘,保证 ACID(最安全,但 I/O 最高)。 |
[mysqld] | innodb_flush_method | O_DIRECT | InnoDB 数据文件 I/O 使用 O_DIRECT,绕过 OS 缓存,避免双缓冲(适合 SSD)。 |
[mysqld] | innodb_io_capacity | 2000 | 设备每秒可处理的 I/O 操作数(IOPS),用于控制后台刷新速率(SSD 推荐值)。 |
[mysqld] | innodb_io_capacity_max | 4000 | 后台任务(如脏页刷新)可突发达到的最大 IOPS。 |
[mysqld] | innodb_read_io_threads | 8 | InnoDB 用于读操作的 I/O 线程数(默认 4,SSD 可适当增加)。 |
[mysqld] | innodb_write_io_threads | 8 | InnoDB 用于写操作的 I/O 线程数(默认 4,SSD 可适当增加)。 |
[mysqld] | innodb_thread_concurrency | 0 | InnoDB 内部线程并发数限制;0 表示不限制,由操作系统调度(MySQL 5.7 推荐值)。 |
[mysqld] | innodb_fast_shutdown | 0 | 关闭时执行完整 purge 和 merge,确保下次启动快速(值 0 = 慢速干净关机)。 |
[mysqld] | innodb_adaptive_hash_index | ON | 启用自适应哈希索引(AHI),对热点索引自动构建哈希索引,加速等值查询。 |
[mysqld] | socket | /usr/local/mysql/mysql.sock | mysqld 监听的 Unix socket 文件路径,供本地客户端连接使用。 |
[mysqld] | pid_file | /usr/local/mysql/mysqld.pid | mysqld 进程 ID 文件路径,用于管理脚本识别进程。 |
[mysqld] | log_error | /mydata/data/mysql-error.log | 错误日志文件路径,记录启动、运行、关闭过程中的关键信息。 |
[mysqld] | innodb_log_file_size | 2G | 单个 redo log 文件大小(总大小 = 此值 × innodb_log_files_in_group)。 |
[mysqld] | innodb_log_files_in_group | 2 | redo log 文件数量(通常为 2,总 redo 空间 = 2 × 2G = 4G)。 |
[mysqld] | innodb_log_buffer_size | 16M | redo log 内存缓冲区大小,大事务可适当调高以减少磁盘写。 |
[mysqld] | server_id | 100 | 服务器唯一标识,在主从复制中必须全局唯一。 |
[mysqld] | log_bin | /mydata/binlog/mysql-bin | 启用二进制日志并指定基础路径和前缀(用于复制、PITR 恢复)。 |
[mysqld] | binlog_format | row | 二进制日志格式为 ROW,记录每一行变更,最安全且兼容性好。 |
[mysqld] | expire_logs_days | 31 | 自动清理 31 天前的 binlog 文件(MySQL 5.7 使用此参数,8.0 改为 binlog_expire_logs_seconds)。 |
[mysqld] | log_bin_trust_function_creators | 1 | 允许用户创建存储函数而不强制要求 DETERMINISTIC 或 READS SQL DATA(方便开发,但有安全风险)。 |
[mysqld] | slow_query_log | on | 启用慢查询日志,记录执行时间超过阈值的 SQL。 |
[mysqld] | long_query_time | 10 | 慢查询阈值为 10 秒(单位:秒,可为小数如 0.1)。 |
[mysqld] | slow_query_log_file | /mydata/data/mysql-slow.log | 慢查询日志文件路径。 |
[mysqld] | skip_name_resolve | 1 | 跳过客户端主机名 DNS 反向解析,提升连接速度并避免 DNS 问题。 |
[mysqld] | event_scheduler | 1 | 启用事件调度器(可执行定时任务,等价于 ON)。 |
[mysqld] | max_allowed_packet | 256M | 单个 SQL 语句或结果集允许的最大大小(影响大 BLOB/批量插入)。 |
[mysqld] | lower_case_table_names | 1 | 表名和数据库名以小写存储并比较(Windows/macOS 兼容模式,Linux 上需初始化时设定)。 |
[mysqld] | performance_schema_max_sql_text_length | 10240 | Performance Schema 中记录的 SQL 文本最大长度(默认 1024,此处扩展至 10KB)。 |
[mysqld] | open_files_limit | 65536 | 设置 mysqld 可打开的最大文件描述符数(需操作系统 ulimit 支持)。 |
[mysqld] | log_timestamps | SYSTEM | 日志时间戳使用系统时区(而非 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_timestamp | on | 禁用 TIMESTAMP 字段的隐式默认行为(如自动更新),需显式声明(MySQL 5.6+ 推荐开启)。 |
[xtrabackup] | defaults_file | /etc/my.cnf | XtraBackup 读取此配置文件获取连接信息(非 mysqld 参数,仅 XtraBackup 使用)。 |
[xtrabackup] | user | bkpuser | XtraBackup 连接 MySQL 所用的用户名。 |
[xtrabackup] | password | Bkpuser_2026 | XtraBackup 用户密码 |
[xtrabackup] | parallel | 4 | XtraBackup 并行备份线程数(加速备份过程)。 |
[xtrabackup] | compress | (无值) | 启用备份压缩(使用 zlib)。 |
[xtrabackup] | compress-threads | 4 | 压缩所用的线程数。 |
✅ [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 实战干货!
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~ ❤️