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

在现代云原生和微服务架构中,MySQL 8.0 凭借其窗口函数、CTE、角色管理、原子 DDL、并行查询等特性,已成为新一代应用的首选数据库。本文将手把手带你用二进制方式 部署一个高度优化、安全可靠的 MySQL 8.0 实例,并集成 Percona XtraBackup 8.0 实现自动化全量/增量备份策略。

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

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

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

mysql5.7 的部署请看这里


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

1. MySQL 社区版(8.0)

前往 MySQL Archive 下载 Generic Linux (glibc 2.17 或 2.28) x86_64 版本的 tar.gz 包。

✅ 关键提示:

  • glibc 2.17 → 对应 RHEL/CentOS 7(el7)
  • glibc 2.28 → 对应 RHEL/CentOS 8+(el8/el9)

选错版本会导致 GLIBC_2.XX not found 错误!

https://downloads.mysql.com/archives/community/

2. Percona XtraBackup(8.0)

⚠️ 重要:XtraBackup 8.0 仅兼容 MySQL 8.0,且需匹配 minor 版本(如 MySQL 8.0.32 需 XtraBackup 8.0.32+)。

下载地址:Percona XtraBackup 8.0 Binary Tarball

3. qpress(压缩工具)

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

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

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


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

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

bash 复制代码
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 争抢;按时间分层,便于日志轮转和清理。


📦 三、解压部署包

bash 复制代码
mkdir -p /usr/local/{mysql,xtrabackup}
# 解压 MySQL
tar -xf /mydata/soft/mysql8.tar.gz --strip-components=1 -C /usr/local/mysql

# 解压 XtraBackup
tar -xf /mydata/soft/xtrabackup.tar.gz --strip-components=1 -C /usr/local/xtrabackup

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

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

1. 用户资源限制(ulimit)

  • 交互式 Shell 生效
bash 复制代码
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
  • 当前会话立即生效
bash 复制代码
ulimit -Hu 16384 -Hn 65536
  • PAM 永久生效
bash 复制代码
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. 内核参数调优

bash 复制代码
echo '
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10

vm.overcommit_memory = 2
vm.overcommit_ratio = 90

vm.swappiness = 0' >> /etc/sysctl.conf
bash 复制代码
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 密集型数据库 至关重要,能显著提升写入稳定性和响应速度。


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

bash 复制代码
# 创建 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(简单粗暴)

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

bash 复制代码
# 临时禁用
setenforce 0 

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

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

bash 复制代码
# 先确认是否已安装了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(性能核心!)

bash 复制代码
# 备份原文件
[ -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,以此类推)

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

bash 复制代码
echo "[client]
socket=/usr/local/mysql/mysql.sock

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

#性能关键参数自动调整
#innodb_dedicated_server=on
#innodb 缓冲池(RAM*70%,适用于一机一库)
innodb_buffer_pool_size=3G
innodb_buffer_pool_instances = 8
innodb_fast_shutdown = 0  
innodb_flush_log_at_trx_commit = 1

# SSD 推荐配置
innodb_flush_method = O_DIRECT
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_read_io_threads = 8
innodb_write_io_threads = 8

#socket位置
socket=/usr/local/mysql/mysql.sock
#pid文件位置
pid_file=/usr/local/mysql/mysqld.pid
#错误日志位置
log_error=/mydata/data/mysql-error.log
#错误日志级别(默认2:warning)
log_error_verbosity=3
#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
binlog_expire_logs_seconds=604800
log_bin_trust_function_creators=1
#slowlog
slow_query_log=on
long_query_time=10
log_slow_extra=on
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
#激活角色
activate_all_roles_on_login=1

##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]
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 UTF8MB4 设置服务器默认字符集为 utf8mb4,支持完整的 Unicode(包括 emoji)。
[mysqld] default_storage_engine InnoDB 设置新建表的默认存储引擎为 InnoDB(事务安全、支持外键)。
[mysqld] user mysql 指定 mysqld 进程运行的操作系统用户(提升安全性,避免以 root 运行)。
[mysqld] port 3306 MySQL 监听的 TCP/IP 端口号,默认为 3306。
[mysqld] basedir /usr/local/mysql MySQL 安装目录(包含 bin、lib、share 等子目录)。
[mysqld] datadir /mydata/data MySQL 数据文件(如 ibdata1、.ibd、.frm 等)的存储目录。
[mysqld] max_connections 1000 允许的最大并发连接数(包括后台线程),超过将拒绝新连接。
[mysqld] table_open_cache 4000 表缓存大小,用于缓存打开的表描述符,减少重复打开/关闭开销。
[mysqld] table_definition_cache 2000 表定义(.frm 或数据字典)缓存数量,避免频繁读取磁盘元数据。
[mysqld] thread_cache_size 100 线程缓存大小,复用线程以减少创建/销毁开销,提升短连接性能。
[mysqld] innodb_buffer_pool_size 3G InnoDB 缓冲池大小,用于缓存数据和索引,是 InnoDB 性能最关键参数。
[mysqld] innodb_buffer_pool_instances 8 将缓冲池划分为 8 个独立实例,减少内部争用,提升并发性能。
[mysqld] innodb_fast_shutdown 0 关闭时执行完整 purge 和 merge,确保下次启动快速(值 0 = 慢速干净关机)。
[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] 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] log_error_verbosity 3 错误日志详细级别:1=错误,2=错误+警告,3=错误+警告+提示信息(MySQL 5.7+ / 8.0)。
[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] binlog_expire_logs_seconds 604800 自动清理 7 天(604800 秒)前的 binlog 文件,防止磁盘爆满。
[mysqld] log_bin_trust_function_creators 1 允许用户创建存储函数而不强制要求 DETERMINISTICREADS SQL DATA(方便开发,但有安全风险)。
[mysqld] slow_query_log on 启用慢查询日志,记录执行时间超过阈值的 SQL。
[mysqld] long_query_time 10 慢查询阈值为 10 秒(单位:秒,可为小数如 0.1)。
[mysqld] log_slow_extra on (MySQL 8.0.14+)在慢日志中额外记录更多统计信息(如内存使用、临时表等)。
[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),便于与系统日志对齐。
[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+ 推荐开启)。
[mysqld] activate_all_roles_on_login 1 用户登录时自动激活其拥有的所有角色(无需手动 SET ROLE)。
[xtrabackup] user bkpuser XtraBackup 连接 MySQL 所用的用户名(需具备 BACKUP_ADMIN 等权限)。
[xtrabackup] password Bkpuser_2026 XtraBackup 用户密码
[xtrabackup] parallel 4 XtraBackup 并行备份线程数(加速备份过程)。
[xtrabackup] compress (无值) 启用备份压缩(使用 zlib)。
[xtrabackup] compress-threads 4 压缩所用的线程数(与 parallel 解耦,可单独设置)。

✅ [xtrabackup] 段:XtraBackup 会读取此段配置,实现免密、并行、压缩备份。


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

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

bash 复制代码
mysqld --initialize-insecure --user=mysql --datadir=/mydata/data 

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

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

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

bash 复制代码
echo '[Unit]
Description=MySQL 8 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]
User=mysql
Type=notify

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

# Execute pre and post scripts as root
PermissionsStartOnly=true

# Start main service
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf

# Use this to switch malloc implementation
EnvironmentFile=-/etc/sysconfig/mysql

# Sets open_files_limit
LimitNOFILE = 10000

#Restart=on-failure
RestartPreventExitStatus=1

# Set enviroment variable MYSQLD_PARENT_PID. This is required for restart.
Environment=MYSQLD_PARENT_PID=1
PrivateTmp=false' > /usr/lib/systemd/system/mysqld.service

✅ Type=notify:MySQL 8.0 使用 Type=notify(而非 5.7 的 forking),通过 sd_notify() 通知 systemd 启动完成。

3. 启动服务

bash 复制代码
systemctl daemon-reload && systemctl enable mysqld
systemctl start mysqld 

🔐 九、安全加固

1. 设置 root 密码

bash 复制代码
mysql -u root --skip-password -e "alter user 'root'@'localhost' identified by 'Wenb1n_dev';"

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

2. 启用密码策略插件

sql 复制代码
#先登陆到mysql中
mysql -uroot -pWenb1n_dev 

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

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

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

sql 复制代码
## 创建一个名为 'u_admin' 的用户,仅允许从本地主机(localhost)连接,并设置密码为 'Wenb1n_dev'
CREATE USER 'u_admin'@'localhost' IDENTIFIED BY 'Wenb1n_dev';

## 授予 'u_admin'@'localhost' 用户对所有数据库和所有表的全部权限,并允许其将权限授予其他用户(即拥有授权权限)
GRANT ALL ON *.* TO 'u_admin'@'localhost' WITH GRANT OPTION;

## 锁定默认的 'root'@'localhost' 账户,使其无法登录(增强安全性,防止直接使用 root 登录)
ALTER USER 'root'@'localhost' ACCOUNT LOCK;

## 创建一个名为 ro_dql 的角色,用于只读查询操作(DQL: Data Query Language)
CREATE ROLE ro_dql;

## 将 SELECT 和 SHOW VIEW 权限授予 ro_dql 角色,使其能查询数据和查看视图定义(适用于所有数据库和表)
GRANT SELECT, SHOW VIEW ON *.* TO ro_dql;

## 创建一个名为 ro_dml 的角色,用于数据操作语言(DML: Data Manipulation Language)相关操作
CREATE ROLE ro_dml;

## 授予 ro_dml 角色一系列 DML 及辅助权限:
## 包括查询、查看视图、查看数据库列表、创建临时对象、索引管理、增删改数据、执行存储过程、
## 读取文件(FILE)、查看进程(PROCESS)、重载配置(RELOAD)、锁定表等(作用于所有数据库和表)
GRANT SELECT, SHOW VIEW, SHOW DATABASES, CREATE, INDEX, INSERT, UPDATE, DELETE, EXECUTE, FILE, PROCESS, RELOAD, LOCK TABLES ON *.* TO ro_dml;

## 创建一个名为 ro_ddl 的角色,用于数据定义语言(DDL: Data Definition Language)相关操作
CREATE ROLE ro_ddl;

## 授予 ro_ddl 角色一系列 DDL 权限:
## 包括创建/删除/修改数据库对象(如表、视图、存储过程、事件、触发器、表空间、临时表等),
## 以及引用外键(REFERENCES)等(作用于所有数据库和表)
GRANT CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TABLESPACE, CREATE TEMPORARY TABLES, CREATE VIEW,
      EVENT, REFERENCES, TRIGGER, DROP, ALTER, ALTER ROUTINE ON *.* TO ro_ddl;

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

💡 提示

  • ro_dqlro_dmlro_ddl 是自定义角色,后续可通过 GRANT ro_dql TO 'some_user'@'host'; 将角色分配给具体用户。
  • 锁定 root 账户后,请确保已有其他具备管理员权限的账户(如这里的 u_admin)可用,否则可能导致无法管理数据库。
  • 生产环境中应谨慎授予 FILEPROCESSRELOAD 等高危权限。

4. 开放防火墙

bash 复制代码
firewall-cmd --permanent --add-port=3306/tcp && firewall-cmd --reload

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


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

sql 复制代码
## 创建一个名为 'demo' 的用户,允许从任意主机('%' 表示所有 IP)连接,并设置密码为 'Power_2025'
CREATE USER 'demo'@'%' IDENTIFIED BY 'Power_2025';

## 将之前定义的角色 ro_dml 和 ro_ddl 授予 'demo'@'%' 用户,
## 使其拥有数据操作(DML)和数据定义(DDL)的广泛权限(具体权限由角色定义决定)
GRANT ro_dml, ro_ddl TO 'demo'@'%';

## 创建一个专用于备份操作的本地用户 'bkpuser',仅允许从 localhost 连接,密码为 'Bkpuser_2020'
CREATE USER 'bkpuser'@'localhost' IDENTIFIED BY 'Bkpuser_2026';

## 授予 'bkpuser'@'localhost' 执行数据库备份所需的最小必要权限:
##   SELECT:用于读取数据;
##   BACKUP_ADMIN:MySQL 8.0+ 中用于执行备份相关操作的动态权限(如使用 mysqlbackup 或克隆插件);
##   RELOAD:用于执行 FLUSH 操作(如刷新日志、缓存等);
##   PROCESS:查看当前运行的线程(用于监控或排除备份干扰);
##   LOCK TABLES:在备份时锁定表以保证一致性;
##   REPLICATION CLIENT:查看主从复制状态(如用于获取二进制日志位置,支持 PITR 点-in-time 恢复)
## 所有权限作用于所有数据库和表(*.*)
GRANT SELECT, BACKUP_ADMIN, RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'bkpuser'@'localhost';

✅ MySQL 8.0 角色优势:

  • 权限集中管理(GRANT role TO user
  • 动态生效(无需 FLUSH PRIVILEGES
  • 支持 activate_all_roles_on_login=ON 自动激活

备份账号权限说明

  • BACKUP_ADMIN:MySQL 8.0 新增权限,专用于备份操作(替代旧版的 SELECT + LOCK TABLES
  • 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 份
    📌 脚本已内置:并发锁、空目录防护、有效性校验、日志记录。
sql 复制代码
首次备份手工执行全量备份
xtrabackup --backup --target-dir=/mydata/backup/xtrabackup/full
sql 复制代码
#!/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 8.0 热备 + 自动化策略
运维友好 标准化目录 + systemd 管理 + 角色授权

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

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


相关推荐
剩下了什么13 小时前
MySQL JSON_SET() 函数
数据库·mysql·json
山峰哥14 小时前
数据库工程与SQL调优——从索引策略到查询优化的深度实践
数据库·sql·性能优化·编辑器
较劲男子汉14 小时前
CANN Runtime零拷贝传输技术源码实战 彻底打通Host与Device的数据传输壁垒
运维·服务器·数据库·cann
java搬砖工-苤-初心不变14 小时前
MySQL 主从复制配置完全指南:从原理到实践
数据库·mysql
WangYaolove131416 小时前
基于python的在线水果销售系统(源码+文档)
python·mysql·django·毕业设计·源码
山岚的运维笔记16 小时前
SQL Server笔记 -- 第18章:Views
数据库·笔记·sql·microsoft·sqlserver
roman_日积跬步-终至千里17 小时前
【LangGraph4j】LangGraph4j 核心概念与图编排原理
java·服务器·数据库
汇智信科17 小时前
打破信息孤岛,重构企业效率:汇智信科企业信息系统一体化运营平台
数据库·重构
野犬寒鸦17 小时前
从零起步学习并发编程 || 第六章:ReentrantLock与synchronized 的辨析及运用
java·服务器·数据库·后端·学习·算法
霖霖总总17 小时前
[小技巧66]当自增主键耗尽:MySQL 主键溢出问题深度解析与雪花算法替代方案
mysql·算法